mirror of
https://github.com/Alfresco/alfresco-ng2-components.git
synced 2025-06-30 18:15:11 +00:00
[ADF-4349] Cloud - task-form-component - Create a new component (#4620)
* [ADF-4349] Created task form cloud * [ADF-4349] Used task-form in demo shell * [ADF-4349] Used task directives * [ADF-4349] Added tests * [ADF-4349] Added documentation * [ADF-4349] Added translation for buttons
This commit is contained in:
parent
bcdfcee397
commit
21fd0299bd
@ -3,23 +3,15 @@
|
||||
<div fxLayout="column" fxFill fxLayoutGap="2px">
|
||||
<div fxLayout="row" fxFill>
|
||||
<div fxLayout="column" fxFlex="80%">
|
||||
<div class="adf-task-control">
|
||||
<button mat-button (click)="goBack()">Cancel</button>
|
||||
<button mat-button color="primary" *ngIf="canCompleteTask()" adf-cloud-complete-task
|
||||
(success)="onCompletedTask()">{{ 'ADF_TASK_LIST.DETAILS.BUTTON.COMPLETE' | translate }}</button>
|
||||
|
||||
<button mat-button color="primary" *ngIf="canClaimTask()" adf-cloud-claim-task
|
||||
(success)="onClaimTask()">{{ 'ADF_TASK_LIST.DETAILS.BUTTON.CLAIM' | translate }}</button>
|
||||
|
||||
<button mat-button color="primary" *ngIf="canUnClaimTask()" adf-cloud-unclaim-task
|
||||
(success)="onUnclaimTask()">{{ 'ADF_TASK_LIST.DETAILS.BUTTON.UNCLAIM' | translate }}</button>
|
||||
</div>
|
||||
<adf-cloud-form *ngIf="hasTaskForm()" fxFlex="100%"
|
||||
<adf-task-form-cloud
|
||||
[appName]="appName"
|
||||
[taskId]="taskId"
|
||||
(formCompleted)="onTaskCompleted()"
|
||||
(cancelClick)="goBack()"
|
||||
(taskClaimed)="onClaimTask()"
|
||||
(taskCompleted)="onTaskCompleted()"
|
||||
(taskUnclaimed)="onUnclaimTask()"
|
||||
(formSaved)="onFormSaved()">
|
||||
</adf-cloud-form>
|
||||
</adf-task-form-cloud>
|
||||
</div>
|
||||
<adf-cloud-task-header fxFlex
|
||||
[appName]="appName"
|
||||
|
@ -15,18 +15,17 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { Component, OnInit } from '@angular/core';
|
||||
import { Component } from '@angular/core';
|
||||
import { ActivatedRoute, Router } from '@angular/router';
|
||||
import { TaskDetailsCloudModel, TaskCloudService, UploadCloudWidgetComponent } from '@alfresco/adf-process-services-cloud';
|
||||
import { UploadCloudWidgetComponent } from '@alfresco/adf-process-services-cloud';
|
||||
import { NotificationService, FormRenderingService } from '@alfresco/adf-core';
|
||||
|
||||
@Component({
|
||||
templateUrl: './task-details-cloud-demo.component.html',
|
||||
styleUrls: ['./task-details-cloud-demo.component.scss']
|
||||
})
|
||||
export class TaskDetailsCloudDemoComponent implements OnInit {
|
||||
export class TaskDetailsCloudDemoComponent {
|
||||
|
||||
taskDetails: TaskDetailsCloudModel;
|
||||
taskId: string;
|
||||
appName: string;
|
||||
|
||||
@ -34,7 +33,6 @@ export class TaskDetailsCloudDemoComponent implements OnInit {
|
||||
private route: ActivatedRoute,
|
||||
private router: Router,
|
||||
private formRenderingService: FormRenderingService,
|
||||
private taskCloudService: TaskCloudService,
|
||||
private notificationService: NotificationService
|
||||
) {
|
||||
this.route.params.subscribe((params) => {
|
||||
@ -47,37 +45,10 @@ export class TaskDetailsCloudDemoComponent implements OnInit {
|
||||
|
||||
}
|
||||
|
||||
ngOnInit() {
|
||||
this.loadTaskDetailsById(this.appName, this.taskId);
|
||||
}
|
||||
|
||||
loadTaskDetailsById(appName: string, taskId: string) {
|
||||
this.taskCloudService.getTaskById(appName, taskId).subscribe(
|
||||
(taskDetails: TaskDetailsCloudModel ) => {
|
||||
this.taskDetails = taskDetails;
|
||||
});
|
||||
}
|
||||
|
||||
isTaskValid(): boolean {
|
||||
return this.appName !== undefined && this.taskId !== undefined;
|
||||
}
|
||||
|
||||
canCompleteTask(): boolean {
|
||||
return this.taskDetails && !this.taskDetails.formKey && this.taskCloudService.canCompleteTask(this.taskDetails);
|
||||
}
|
||||
|
||||
canClaimTask(): boolean {
|
||||
return this.taskDetails && this.taskCloudService.canClaimTask(this.taskDetails);
|
||||
}
|
||||
|
||||
canUnClaimTask(): boolean {
|
||||
return this.taskDetails && this.taskCloudService.canUnclaimTask(this.taskDetails);
|
||||
}
|
||||
|
||||
hasTaskForm(): boolean {
|
||||
return this.taskDetails && this.taskDetails.formKey;
|
||||
}
|
||||
|
||||
goBack() {
|
||||
this.router.navigate([`/cloud/${this.appName}/`]);
|
||||
}
|
||||
|
@ -0,0 +1,63 @@
|
||||
---
|
||||
Title: Form cloud component
|
||||
Added: v3.2.0
|
||||
Status: Active
|
||||
Last reviewed: 2019-04-17
|
||||
---
|
||||
|
||||
# [Task form cloud component](../../../lib/process-services-cloud/src/lib/form/components/task-form-cloud.component.ts "Defined in task-form-cloud.component.ts")
|
||||
|
||||
Shows a [`form`](../../../lib/process-services-cloud/src/lib/form/models/form-cloud.model.ts) for a task.
|
||||
|
||||
## Contents
|
||||
|
||||
- [Basic Usage](#basic-usage)
|
||||
- [Class members](#class-members)
|
||||
- [Properties](#properties)
|
||||
- [Events](#events)
|
||||
- [See also](#see-also)
|
||||
|
||||
## Basic Usage
|
||||
|
||||
```html
|
||||
<adf-task-form-cloud
|
||||
[appName]="appName"
|
||||
[taskId]="taskId">
|
||||
</adf-task-form-cloud>
|
||||
```
|
||||
|
||||
|
||||
## Class members
|
||||
|
||||
### Properties
|
||||
|
||||
| Name | Type | Default value | Description |
|
||||
| ---- | ---- | ------------- | ----------- |
|
||||
| appName | `string` | | App id to fetch corresponding form and values. |
|
||||
| taskId | `string` | | Task id to fetch corresponding form and values. |
|
||||
| showRefreshButton | `boolean` | false | Toggle rendering of the `Refresh` button. |
|
||||
| showValidationIcon | `boolean` | true | Toggle rendering of the `Validation` icon. |
|
||||
| showCancelButton | `boolean` | true | Toggle rendering of the `Cancel` outcome button. |
|
||||
| showCompleteButton | `boolean` | true | Toggle rendering of the `Complete` outcome button. |
|
||||
| showSaveButton | `boolean` | true | Toggle rendering of the `Save` outcome button. |
|
||||
| readOnly | `boolean` | false | Toggle readonly state of the task. |
|
||||
|
||||
|
||||
### Events
|
||||
|
||||
| Name | Type | Description |
|
||||
| ---- | ---- | ----------- |
|
||||
| formSaved | [`EventEmitter`](https://angular.io/api/core/EventEmitter)`<`[`FormCloud`](../../../lib/process-services-cloud/src/lib/form/models/form-cloud.model.ts)`>` | Emitted when the form is saved. |
|
||||
| formCompleted | [`EventEmitter`](https://angular.io/api/core/EventEmitter)`<`[`FormCloud`](../../../lib/process-services-cloud/src/lib/form/models/form-cloud.model.ts)`>` | Emitted when the form is submitted with the `Complete` outcome. |
|
||||
| taskCompleted | [`EventEmitter`](https://angular.io/api/core/EventEmitter)`<`string`>` | Emitted when the task is completed. |
|
||||
| taskClaimed | [`EventEmitter`](https://angular.io/api/core/EventEmitter)`<`string`>` | Emitted when the task is claimed. |
|
||||
| taskUnclaimed | [`EventEmitter`](https://angular.io/api/core/EventEmitter)`<`string`>` | Emitted when the task is unclaimed. |
|
||||
| cancelClick | [`EventEmitter`](https://angular.io/api/core/EventEmitter)`<`string`>` | Emitted when the cancel button is clicked. |
|
||||
| error | [`EventEmitter`](https://angular.io/api/core/EventEmitter)`<any>` | Emitted when any error occurs. |
|
||||
|
||||
|
||||
## See also
|
||||
|
||||
- [Form component](./form-cloud.component.md)
|
||||
- [Form field model](../../core/models/form-field.model.md)
|
||||
- [Form cloud service](../services/form-cloud.service.md)
|
@ -1,6 +1,9 @@
|
||||
{
|
||||
"SAVE": "SAVE",
|
||||
"COMPLETE": "COMPLETE",
|
||||
"CANCEL": "CANCEL",
|
||||
"CLAIM": "CLAIM",
|
||||
"UNCLAIM": "UNCLAIM",
|
||||
"START PROCESS": "START PROCESS",
|
||||
"FORM": {
|
||||
"START_FORM": {
|
||||
|
@ -750,4 +750,52 @@ describe('FormCloudComponent', () => {
|
||||
radioFieldById = formFields.find((field) => field.id === 'radiobuttons1');
|
||||
expect(radioFieldById.value).toBe('option_2');
|
||||
});
|
||||
|
||||
it('should emit executeOutcome on [claim] outcome click', (done) => {
|
||||
const formModel = new FormCloud();
|
||||
const outcome = new FormOutcomeModel(<any> formModel, {
|
||||
id: FormCloud.CLAIM_OUTCOME,
|
||||
name: 'CLAIM',
|
||||
isSystem: true
|
||||
});
|
||||
|
||||
formComponent.form = formModel;
|
||||
formComponent.executeOutcome.subscribe(() => {
|
||||
done();
|
||||
});
|
||||
|
||||
formComponent.onOutcomeClicked(outcome);
|
||||
});
|
||||
|
||||
it('should emit executeOutcome on [unclaim] outcome click', (done) => {
|
||||
const formModel = new FormCloud();
|
||||
const outcome = new FormOutcomeModel(<any> formModel, {
|
||||
id: FormCloud.UNCLAIM_OUTCOME,
|
||||
name: 'UNCLAIM',
|
||||
isSystem: true
|
||||
});
|
||||
|
||||
formComponent.form = formModel;
|
||||
formComponent.executeOutcome.subscribe(() => {
|
||||
done();
|
||||
});
|
||||
|
||||
formComponent.onOutcomeClicked(outcome);
|
||||
});
|
||||
|
||||
it('should emit executeOutcome on [cancel] outcome click', (done) => {
|
||||
const formModel = new FormCloud();
|
||||
const outcome = new FormOutcomeModel(<any> formModel, {
|
||||
id: FormCloud.CANCEL_OUTCOME,
|
||||
name: 'CANCEL',
|
||||
isSystem: true
|
||||
});
|
||||
|
||||
formComponent.form = formModel;
|
||||
formComponent.executeOutcome.subscribe(() => {
|
||||
done();
|
||||
});
|
||||
|
||||
formComponent.onOutcomeClicked(outcome);
|
||||
});
|
||||
});
|
||||
|
@ -53,6 +53,18 @@ export class FormCloudComponent extends FormBaseComponent implements OnChanges {
|
||||
@Input()
|
||||
data: TaskVariableCloud[];
|
||||
|
||||
/** Toggle rendering of the `Cancel` outcome button. */
|
||||
@Input()
|
||||
showCancelButton = false;
|
||||
|
||||
/** Toggle rendering of the `Claim` outcome button. */
|
||||
@Input()
|
||||
showClaimButton = false;
|
||||
|
||||
/** Toggle rendering of the `Unclaim` outcome button. */
|
||||
@Input()
|
||||
showUnclaimButton = false;
|
||||
|
||||
/** Emitted when the form is submitted with the `Save` or custom outcomes. */
|
||||
@Output()
|
||||
formSaved: EventEmitter<FormCloud> = new EventEmitter<FormCloud>();
|
||||
@ -147,6 +159,7 @@ export class FormCloudComponent extends FormBaseComponent implements OnChanges {
|
||||
(data) => {
|
||||
this.data = data[1];
|
||||
const parsedForm = this.parseForm(data[0]);
|
||||
this.appendCustomOutcomes(parsedForm);
|
||||
this.visibilityService.refreshVisibility(<any> parsedForm);
|
||||
parsedForm.validateForm();
|
||||
this.form = parsedForm;
|
||||
@ -178,6 +191,7 @@ export class FormCloudComponent extends FormBaseComponent implements OnChanges {
|
||||
.subscribe(
|
||||
(form) => {
|
||||
const parsedForm = this.parseForm(form);
|
||||
this.appendCustomOutcomes(parsedForm);
|
||||
this.visibilityService.refreshVisibility(<any> parsedForm);
|
||||
parsedForm.validateForm();
|
||||
this.form = parsedForm;
|
||||
@ -293,4 +307,39 @@ export class FormCloudComponent extends FormBaseComponent implements OnChanges {
|
||||
|
||||
protected storeFormAsMetadata() {
|
||||
}
|
||||
|
||||
private appendCustomOutcomes(form: FormCloud): FormCloud {
|
||||
|
||||
if (this.showClaimButton) {
|
||||
const claimOutcome = new FormOutcomeModel(<any> form, {
|
||||
id: FormCloud.CLAIM_OUTCOME,
|
||||
name: 'CLAIM',
|
||||
isSystem: true
|
||||
});
|
||||
|
||||
form.outcomes.unshift(claimOutcome);
|
||||
}
|
||||
|
||||
if (this.showUnclaimButton) {
|
||||
const unclaimOutcome = new FormOutcomeModel(<any> form, {
|
||||
id: FormCloud.UNCLAIM_OUTCOME,
|
||||
name: 'UNCLAIM',
|
||||
isSystem: true
|
||||
});
|
||||
|
||||
form.outcomes.unshift(unclaimOutcome);
|
||||
}
|
||||
|
||||
if (this.showCancelButton) {
|
||||
const cancelOutcome = new FormOutcomeModel(<any> form, {
|
||||
id: FormCloud.CANCEL_OUTCOME,
|
||||
name: 'CANCEL',
|
||||
isSystem: true
|
||||
});
|
||||
|
||||
form.outcomes.unshift(cancelOutcome);
|
||||
}
|
||||
|
||||
return form;
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,56 @@
|
||||
<div *ngIf="taskDetails">
|
||||
<adf-cloud-form *ngIf="hasForm(); else withoutForm"
|
||||
[appName]="appName"
|
||||
[taskId]="taskId"
|
||||
[readOnly]="isReadOnly()"
|
||||
[showRefreshButton]="showRefreshButton"
|
||||
[showValidationIcon]="showValidationIcon"
|
||||
[showCompleteButton]="canCompleteTask()"
|
||||
[showSaveButton]="canCompleteTask()"
|
||||
[showCancelButton]="showCancelButton"
|
||||
[showClaimButton]="canClaimTask()"
|
||||
[showUnclaimButton]="canUnclaimTask()"
|
||||
(executeOutcome)="onExecuteOutcome($event.outcome)"
|
||||
(formSaved)="onFormSaved($event)"
|
||||
(formCompleted)="onFormCompleted($event)"
|
||||
(formError)="onError($event)">
|
||||
</adf-cloud-form>
|
||||
|
||||
<ng-template #withoutForm>
|
||||
<mat-card class="adf-task-form-container">
|
||||
<mat-card-header>
|
||||
<mat-card-title>
|
||||
<h4>
|
||||
<span class="adf-form-title">
|
||||
{{taskDetails.name}}
|
||||
<ng-container *ngIf="!taskDetails.name">
|
||||
{{'FORM.FORM_RENDERER.NAMELESS_TASK' | translate}}
|
||||
</ng-container>
|
||||
</span>
|
||||
</h4>
|
||||
</mat-card-title>
|
||||
</mat-card-header>
|
||||
<mat-card-content>
|
||||
<adf-empty-content
|
||||
[icon]="'description'"
|
||||
[title]="'ADF_CLOUD_TASK_FORM.EMPTY_FORM.TITLE'"
|
||||
[subtitle]="'ADF_CLOUD_TASK_FORM.EMPTY_FORM.SUBTITLE'">
|
||||
</adf-empty-content>
|
||||
</mat-card-content>
|
||||
<mat-card-actions class="adf-task-form-actions">
|
||||
<button mat-button *ngIf="showCancelButton" id="adf-cloud-cancel-task" (click)="onCancelClick()">
|
||||
{{'ADF_CLOUD_TASK_FORM.EMPTY_FORM.BUTTONS.CANCEL' | translate}}
|
||||
</button>
|
||||
<button mat-button *ngIf="canClaimTask()" adf-cloud-claim-task [appName]="appName" [taskId]="taskId" (success)="onClaimTask()">
|
||||
{{'ADF_CLOUD_TASK_FORM.EMPTY_FORM.BUTTONS.CLAIM' | translate}}
|
||||
</button>
|
||||
<button mat-button *ngIf="canUnclaimTask()" adf-cloud-unclaim-task [appName]="appName" [taskId]="taskId" (success)="onUnclaimTask()">
|
||||
{{'ADF_CLOUD_TASK_FORM.EMPTY_FORM.BUTTONS.UNCLAIM' | translate}}
|
||||
</button>
|
||||
<button mat-button *ngIf="canCompleteTask()" adf-cloud-complete-task [appName]="appName" [taskId]="taskId" (success)="onCompleteTask()" color="primary">
|
||||
{{'ADF_CLOUD_TASK_FORM.EMPTY_FORM.BUTTONS.COMPLETE' | translate}}
|
||||
</button>
|
||||
</mat-card-actions>
|
||||
</mat-card>
|
||||
</ng-template>
|
||||
</div>
|
@ -0,0 +1,32 @@
|
||||
@mixin adf-task-form-cloud-theme($theme) {
|
||||
|
||||
$config: mat-typography-config();
|
||||
|
||||
.adf-task-form {
|
||||
&-container {
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
&-actions {
|
||||
float: right;
|
||||
padding-bottom: 25px !important;
|
||||
padding-right: 25px !important;
|
||||
|
||||
& .mat-button {
|
||||
height: 36px;
|
||||
border-radius: 5px;
|
||||
|
||||
}
|
||||
|
||||
& .mat-button-wrapper {
|
||||
width: 58px;
|
||||
height: 20px;
|
||||
opacity: 0.54;
|
||||
font-size: mat-font-size($config, body-2);
|
||||
font-weight: bold;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
@ -0,0 +1,345 @@
|
||||
/*!
|
||||
* @license
|
||||
* Copyright 2019 Alfresco Software, Ltd.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { ProcessServiceCloudTestingModule } from '../../testing/process-service-cloud.testing.module';
|
||||
import { FormCloudModule } from '../form-cloud.module';
|
||||
import { ComponentFixture, TestBed, async } from '@angular/core/testing';
|
||||
import { TaskFormCloudComponent } from './task-form-cloud.component';
|
||||
import { setupTestBed, IdentityUserService } from '@alfresco/adf-core';
|
||||
import { TaskCloudService, TaskDetailsCloudModel } from '../../task/public-api';
|
||||
import { of } from 'rxjs';
|
||||
import { DebugElement, CUSTOM_ELEMENTS_SCHEMA, SimpleChange } from '@angular/core';
|
||||
import { By } from '@angular/platform-browser';
|
||||
|
||||
const taskDetails = {
|
||||
appName: 'simple-app',
|
||||
assignee: 'admin.adf',
|
||||
completedDate: null,
|
||||
createdDate: 1555419255340,
|
||||
description: null,
|
||||
formKey: null,
|
||||
id: 'bd6b1741-6046-11e9-80f0-0a586460040d',
|
||||
name: 'Task1',
|
||||
owner: 'admin.adf',
|
||||
standAlone: true,
|
||||
status: 'ASSIGNED'
|
||||
};
|
||||
|
||||
describe('TaskFormCloudComponent', () => {
|
||||
|
||||
let taskCloudService: TaskCloudService;
|
||||
let identityUserService: IdentityUserService;
|
||||
|
||||
let getTaskSpy: jasmine.Spy;
|
||||
let getCurrentUserSpy: jasmine.Spy;
|
||||
let debugElement: DebugElement;
|
||||
|
||||
let component: TaskFormCloudComponent;
|
||||
let fixture: ComponentFixture<TaskFormCloudComponent>;
|
||||
|
||||
setupTestBed({
|
||||
imports: [ProcessServiceCloudTestingModule, FormCloudModule],
|
||||
schemas: [CUSTOM_ELEMENTS_SCHEMA]
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
taskDetails.status = 'ASSIGNED';
|
||||
identityUserService = TestBed.get(IdentityUserService);
|
||||
getCurrentUserSpy = spyOn(identityUserService, 'getCurrentUserInfo').and.returnValue({username: 'admin.adf'});
|
||||
taskCloudService = TestBed.get(TaskCloudService);
|
||||
getTaskSpy = spyOn(taskCloudService, 'getTaskById').and.returnValue(of(new TaskDetailsCloudModel(taskDetails)));
|
||||
|
||||
fixture = TestBed.createComponent(TaskFormCloudComponent);
|
||||
debugElement = fixture.debugElement;
|
||||
component = fixture.componentInstance;
|
||||
|
||||
});
|
||||
|
||||
it('should create TaskFormCloudComponent ', () => {
|
||||
expect(component instanceof TaskFormCloudComponent).toBe(true);
|
||||
});
|
||||
|
||||
describe('Complete button', () => {
|
||||
it('should show complete button when status is ASSIGNED', async(() => {
|
||||
component.appName = 'app1';
|
||||
component.taskId = 'task1';
|
||||
|
||||
component.loadTask();
|
||||
fixture.detectChanges();
|
||||
fixture.whenStable().then(() => {
|
||||
const completeBtn = debugElement.query(By.css('[adf-cloud-complete-task]'));
|
||||
expect(completeBtn.nativeElement).toBeDefined();
|
||||
});
|
||||
}));
|
||||
|
||||
it('should not show complete button when status is ASSIGNED but assigned to a different person', async(() => {
|
||||
component.appName = 'app1';
|
||||
component.taskId = 'task1';
|
||||
|
||||
getCurrentUserSpy.and.returnValue({});
|
||||
|
||||
component.loadTask();
|
||||
fixture.detectChanges();
|
||||
fixture.whenStable().then(() => {
|
||||
const completeBtn = debugElement.query(By.css('[adf-cloud-complete-task]'));
|
||||
expect(completeBtn).toBeNull();
|
||||
});
|
||||
}));
|
||||
|
||||
it('should not show complete button when showCompleteButton=false', async(() => {
|
||||
component.appName = 'app1';
|
||||
component.taskId = 'task1';
|
||||
component.showCompleteButton = false;
|
||||
|
||||
component.loadTask();
|
||||
fixture.detectChanges();
|
||||
fixture.whenStable().then(() => {
|
||||
const completeBtn = debugElement.query(By.css('[adf-cloud-complete-task]'));
|
||||
expect(completeBtn).toBeNull();
|
||||
});
|
||||
}));
|
||||
});
|
||||
|
||||
describe('Claim/Unclaim buttons', () => {
|
||||
it('should show unclaim button when status is ASSIGNED', async(() => {
|
||||
component.appName = 'app1';
|
||||
component.taskId = 'task1';
|
||||
|
||||
component.loadTask();
|
||||
fixture.detectChanges();
|
||||
fixture.whenStable().then(() => {
|
||||
const unclaimBtn = debugElement.query(By.css('[adf-cloud-unclaim-task]'));
|
||||
expect(unclaimBtn.nativeElement).toBeDefined();
|
||||
});
|
||||
}));
|
||||
|
||||
it('should not show unclaim button when status is ASSIGNED but assigned to different person', async(() => {
|
||||
component.appName = 'app1';
|
||||
component.taskId = 'task1';
|
||||
|
||||
getCurrentUserSpy.and.returnValue({});
|
||||
|
||||
component.loadTask();
|
||||
fixture.detectChanges();
|
||||
fixture.whenStable().then(() => {
|
||||
const unclaimBtn = debugElement.query(By.css('[adf-cloud-unclaim-task]'));
|
||||
expect(unclaimBtn).toBeNull();
|
||||
});
|
||||
}));
|
||||
|
||||
it('should not show unclaim button when status is not ASSIGNED', async(() => {
|
||||
component.appName = 'app1';
|
||||
component.taskId = 'task1';
|
||||
taskDetails.status = '';
|
||||
getTaskSpy.and.returnValue(of(new TaskDetailsCloudModel(taskDetails)));
|
||||
|
||||
component.loadTask();
|
||||
fixture.detectChanges();
|
||||
fixture.whenStable().then(() => {
|
||||
const unclaimBtn = debugElement.query(By.css('[adf-cloud-unclaim-task]'));
|
||||
expect(unclaimBtn).toBeNull();
|
||||
});
|
||||
}));
|
||||
|
||||
it('should show claim button when status is CREATED', async(() => {
|
||||
component.appName = 'app1';
|
||||
component.taskId = 'task1';
|
||||
taskDetails.status = 'CREATED';
|
||||
getTaskSpy.and.returnValue(of(new TaskDetailsCloudModel(taskDetails)));
|
||||
|
||||
component.loadTask();
|
||||
fixture.detectChanges();
|
||||
fixture.whenStable().then(() => {
|
||||
const claimBtn = debugElement.query(By.css('[adf-cloud-claim-task]'));
|
||||
expect(claimBtn.nativeElement).toBeDefined();
|
||||
});
|
||||
}));
|
||||
|
||||
it('should not show claim button when status is not CREATED', async(() => {
|
||||
component.appName = 'app1';
|
||||
component.taskId = 'task1';
|
||||
taskDetails.status = '';
|
||||
getTaskSpy.and.returnValue(of(new TaskDetailsCloudModel(taskDetails)));
|
||||
|
||||
component.loadTask();
|
||||
fixture.detectChanges();
|
||||
fixture.whenStable().then(() => {
|
||||
const claimBtn = debugElement.query(By.css('[adf-cloud-claim-task]'));
|
||||
expect(claimBtn).toBeNull();
|
||||
});
|
||||
}));
|
||||
});
|
||||
|
||||
describe('Cancel button', () => {
|
||||
it('should show cancel button by default', async(() => {
|
||||
component.appName = 'app1';
|
||||
component.taskId = 'task1';
|
||||
|
||||
component.loadTask();
|
||||
fixture.detectChanges();
|
||||
fixture.whenStable().then(() => {
|
||||
const cancelBtn = debugElement.query(By.css('#adf-cloud-cancel-task'));
|
||||
expect(cancelBtn.nativeElement).toBeDefined();
|
||||
});
|
||||
}));
|
||||
|
||||
it('should not show cancel button when showCancelButton=false', async(() => {
|
||||
component.appName = 'app1';
|
||||
component.taskId = 'task1';
|
||||
component.showCancelButton = false;
|
||||
|
||||
component.loadTask();
|
||||
fixture.detectChanges();
|
||||
fixture.whenStable().then(() => {
|
||||
const cancelBtn = debugElement.query(By.css('#adf-cloud-cancel-task'));
|
||||
expect(cancelBtn).toBeNull();
|
||||
});
|
||||
}));
|
||||
});
|
||||
|
||||
describe('Inputs', () => {
|
||||
it('should not show complete/claim/unclaim buttons when readOnly=true', async(() => {
|
||||
component.appName = 'app1';
|
||||
component.taskId = 'task1';
|
||||
component.readOnly = true;
|
||||
|
||||
component.loadTask();
|
||||
fixture.detectChanges();
|
||||
fixture.whenStable().then(() => {
|
||||
const completeBtn = debugElement.query(By.css('[adf-cloud-complete-task]'));
|
||||
expect(completeBtn).toBeNull();
|
||||
|
||||
const claimBtn = debugElement.query(By.css('[adf-cloud-claim-task]'));
|
||||
expect(claimBtn).toBeNull();
|
||||
|
||||
const unclaimBtn = debugElement.query(By.css('[adf-cloud-unclaim-task]'));
|
||||
expect(unclaimBtn).toBeNull();
|
||||
|
||||
const cancelBtn = debugElement.query(By.css('#adf-cloud-cancel-task'));
|
||||
expect(cancelBtn.nativeElement).toBeDefined();
|
||||
});
|
||||
}));
|
||||
|
||||
it('should load data when appName changes', () => {
|
||||
component.taskId = 'task1';
|
||||
component.ngOnChanges({ appName: new SimpleChange(null, 'app1', false) });
|
||||
expect(getTaskSpy).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should load data when taskId changes', () => {
|
||||
component.appName = 'app1';
|
||||
component.ngOnChanges({ taskId: new SimpleChange(null, 'task1', false) });
|
||||
expect(getTaskSpy).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should not load data when appName changes and taskId is not defined', () => {
|
||||
component.ngOnChanges({ appName: new SimpleChange(null, 'app1', false) });
|
||||
expect(getTaskSpy).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should not load data when taskId changes and appName is not defined', () => {
|
||||
component.ngOnChanges({ taskId: new SimpleChange(null, 'task1', false) });
|
||||
expect(getTaskSpy).not.toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
|
||||
describe('Events', () => {
|
||||
it('should emit cancelClick when cancel button is clicked', (done) => {
|
||||
component.appName = 'app1';
|
||||
component.taskId = 'task1';
|
||||
|
||||
component.cancelClick.subscribe(() => {
|
||||
done();
|
||||
});
|
||||
|
||||
component.loadTask();
|
||||
fixture.detectChanges();
|
||||
const cancelBtn = debugElement.query(By.css('#adf-cloud-cancel-task'));
|
||||
|
||||
cancelBtn.nativeElement.click();
|
||||
});
|
||||
|
||||
it('should emit taskCompleted when task is completed', (done) => {
|
||||
|
||||
spyOn(taskCloudService, 'completeTask').and.returnValue(of({}));
|
||||
|
||||
component.appName = 'app1';
|
||||
component.taskId = 'task1';
|
||||
|
||||
component.taskCompleted.subscribe(() => {
|
||||
done();
|
||||
});
|
||||
|
||||
component.loadTask();
|
||||
fixture.detectChanges();
|
||||
const completeBtn = debugElement.query(By.css('[adf-cloud-complete-task]'));
|
||||
|
||||
completeBtn.nativeElement.click();
|
||||
});
|
||||
|
||||
it('should emit taskClaimed when task is claimed', (done) => {
|
||||
spyOn(taskCloudService, 'claimTask').and.returnValue(of({}));
|
||||
taskDetails.status = 'CREATED';
|
||||
getTaskSpy.and.returnValue(of(new TaskDetailsCloudModel(taskDetails)));
|
||||
|
||||
component.appName = 'app1';
|
||||
component.taskId = 'task1';
|
||||
|
||||
component.taskClaimed.subscribe(() => {
|
||||
done();
|
||||
});
|
||||
|
||||
component.loadTask();
|
||||
fixture.detectChanges();
|
||||
const claimBtn = debugElement.query(By.css('[adf-cloud-claim-task]'));
|
||||
|
||||
claimBtn.nativeElement.click();
|
||||
});
|
||||
|
||||
it('should emit taskUnclaimed when task is unclaimed', (done) => {
|
||||
spyOn(taskCloudService, 'unclaimTask').and.returnValue(of({}));
|
||||
taskDetails.status = 'ASSIGNED';
|
||||
getTaskSpy.and.returnValue(of(new TaskDetailsCloudModel(taskDetails)));
|
||||
|
||||
component.appName = 'app1';
|
||||
component.taskId = 'task1';
|
||||
|
||||
component.taskUnclaimed.subscribe(() => {
|
||||
done();
|
||||
});
|
||||
|
||||
component.loadTask();
|
||||
fixture.detectChanges();
|
||||
const unclaimBtn = debugElement.query(By.css('[adf-cloud-unclaim-task]'));
|
||||
|
||||
unclaimBtn.nativeElement.click();
|
||||
});
|
||||
|
||||
it('should emit error when error occurs', (done) => {
|
||||
component.appName = 'app1';
|
||||
component.taskId = 'task1';
|
||||
|
||||
component.error.subscribe(() => {
|
||||
done();
|
||||
});
|
||||
|
||||
component.onError({});
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
});
|
@ -0,0 +1,191 @@
|
||||
/*!
|
||||
* @license
|
||||
* Copyright 2019 Alfresco Software, Ltd.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import {
|
||||
Component, EventEmitter, Input, OnChanges,
|
||||
Output, SimpleChanges
|
||||
} from '@angular/core';
|
||||
import { FormCloud } from '../models/form-cloud.model';
|
||||
import { TaskDetailsCloudModel, TaskCloudService } from '../../task/public-api';
|
||||
import { IdentityUserService, FormOutcomeModel } from '@alfresco/adf-core';
|
||||
|
||||
@Component({
|
||||
selector: 'adf-task-form-cloud',
|
||||
templateUrl: './task-form-cloud.component.html',
|
||||
styleUrls: ['./task-form-cloud.component.scss']
|
||||
})
|
||||
export class TaskFormCloudComponent implements OnChanges {
|
||||
|
||||
/** App id to fetch corresponding form and values. */
|
||||
@Input()
|
||||
appName: string;
|
||||
|
||||
/** Task id to fetch corresponding form and values. */
|
||||
@Input()
|
||||
taskId: string;
|
||||
|
||||
/** Toggle rendering of the `Refresh` button. */
|
||||
@Input()
|
||||
showRefreshButton = false;
|
||||
|
||||
/** Toggle rendering of the `Validation` icon. */
|
||||
@Input()
|
||||
showValidationIcon = true;
|
||||
|
||||
/** Toggle rendering of the `Cancel` button. */
|
||||
@Input()
|
||||
showCancelButton = true;
|
||||
|
||||
/** Toggle rendering of the `Complete` button. */
|
||||
@Input()
|
||||
showCompleteButton = true;
|
||||
|
||||
/** Toggle readonly state of the task. */
|
||||
@Input()
|
||||
readOnly = false;
|
||||
|
||||
/** Emitted when the form is saved. */
|
||||
@Output()
|
||||
formSaved: EventEmitter<FormCloud> = new EventEmitter<FormCloud>();
|
||||
|
||||
/** Emitted when the form is submitted with the `Complete` outcome. */
|
||||
@Output()
|
||||
formCompleted: EventEmitter<FormCloud> = new EventEmitter<FormCloud>();
|
||||
|
||||
/** Emitted when the task is completed. */
|
||||
@Output()
|
||||
taskCompleted: EventEmitter<string> = new EventEmitter<string>();
|
||||
|
||||
/** Emitted when the task is claimed. */
|
||||
@Output()
|
||||
taskClaimed: EventEmitter<string> = new EventEmitter<string>();
|
||||
|
||||
/** Emitted when the task is unclaimed. */
|
||||
@Output()
|
||||
taskUnclaimed: EventEmitter<string> = new EventEmitter<string>();
|
||||
|
||||
/** Emitted when the cancel button is clicked. */
|
||||
@Output()
|
||||
cancelClick: EventEmitter<string> = new EventEmitter<string>();
|
||||
|
||||
/** Emitted when any error occurs. */
|
||||
@Output()
|
||||
error: EventEmitter<any> = new EventEmitter<any>();
|
||||
|
||||
taskDetails: TaskDetailsCloudModel;
|
||||
|
||||
constructor(
|
||||
private taskCloudService: TaskCloudService,
|
||||
private identityUserService: IdentityUserService) {
|
||||
|
||||
}
|
||||
|
||||
ngOnChanges(changes: SimpleChanges) {
|
||||
const appName = changes['appName'];
|
||||
if (appName && appName.currentValue && this.taskId) {
|
||||
this.loadTask();
|
||||
return;
|
||||
}
|
||||
|
||||
const taskId = changes['taskId'];
|
||||
if (taskId && taskId.currentValue && this.appName) {
|
||||
this.loadTask();
|
||||
return;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
loadTask() {
|
||||
this.taskCloudService.getTaskById(this.appName, this.taskId).subscribe((details: TaskDetailsCloudModel) => {
|
||||
this.taskDetails = details;
|
||||
});
|
||||
}
|
||||
|
||||
hasForm(): boolean {
|
||||
return this.taskDetails && !!this.taskDetails.formKey;
|
||||
}
|
||||
|
||||
canCompleteTask(): boolean {
|
||||
return this.showCompleteButton && !this.readOnly && this.taskCloudService.canCompleteTask(this.taskDetails);
|
||||
}
|
||||
|
||||
canClaimTask(): boolean {
|
||||
return !this.readOnly && this.taskCloudService.canClaimTask(this.taskDetails);
|
||||
}
|
||||
|
||||
canUnclaimTask(): boolean {
|
||||
return !this.readOnly && this.taskCloudService.canUnclaimTask(this.taskDetails);
|
||||
}
|
||||
|
||||
isReadOnly(): boolean {
|
||||
return this.readOnly || this.taskDetails.isCompleted();
|
||||
}
|
||||
|
||||
onCompleteTask() {
|
||||
this.taskCompleted.emit(this.taskId);
|
||||
}
|
||||
|
||||
onClaimTask() {
|
||||
this.taskClaimed.emit(this.taskId);
|
||||
}
|
||||
|
||||
onUnclaimTask() {
|
||||
this.taskUnclaimed.emit(this.taskId);
|
||||
}
|
||||
|
||||
onCancelClick() {
|
||||
this.cancelClick.emit(this.taskId);
|
||||
}
|
||||
|
||||
claimTask() {
|
||||
const currentUser = this.identityUserService.getCurrentUserInfo().username;
|
||||
this.taskCloudService.claimTask(this.appName, this.taskId, currentUser).subscribe(
|
||||
() => {
|
||||
this.taskClaimed.emit(this.taskId);
|
||||
});
|
||||
}
|
||||
|
||||
unclaimTask() {
|
||||
this.taskCloudService.unclaimTask(this.appName, this.taskId).subscribe(
|
||||
() => {
|
||||
this.taskUnclaimed.emit(this.taskId);
|
||||
});
|
||||
}
|
||||
|
||||
onExecuteOutcome(outcome: FormOutcomeModel) {
|
||||
if (outcome.id === FormCloud.CANCEL_OUTCOME) {
|
||||
this.onCancelClick();
|
||||
} else if (outcome.id === FormCloud.CLAIM_OUTCOME) {
|
||||
this.claimTask();
|
||||
} else if (outcome.id === FormCloud.UNCLAIM_OUTCOME) {
|
||||
this.unclaimTask();
|
||||
}
|
||||
}
|
||||
|
||||
onFormSaved(form: FormCloud) {
|
||||
this.formSaved.emit(form);
|
||||
}
|
||||
|
||||
onFormCompleted(form: FormCloud) {
|
||||
this.formCompleted.emit(form);
|
||||
this.taskCompleted.emit(this.taskId);
|
||||
}
|
||||
|
||||
onError(data: any) {
|
||||
this.error.emit(data);
|
||||
}
|
||||
}
|
@ -23,6 +23,8 @@ import { FormsModule, ReactiveFormsModule } from '@angular/forms';
|
||||
import { FormCloudComponent } from './components/form-cloud.component';
|
||||
import { UploadCloudWidgetComponent } from './components/upload-cloud.widget';
|
||||
import { MaterialModule } from '../material.module';
|
||||
import { TaskFormCloudComponent } from './components/task-form-cloud.component';
|
||||
import { TaskModule } from '../task/task.module';
|
||||
|
||||
@NgModule({
|
||||
imports: [
|
||||
@ -34,14 +36,15 @@ import { MaterialModule } from '../material.module';
|
||||
FormsModule,
|
||||
ReactiveFormsModule,
|
||||
FormBaseModule,
|
||||
CoreModule
|
||||
CoreModule,
|
||||
TaskModule
|
||||
],
|
||||
declarations: [FormCloudComponent, UploadCloudWidgetComponent],
|
||||
declarations: [FormCloudComponent, UploadCloudWidgetComponent, TaskFormCloudComponent],
|
||||
entryComponents: [
|
||||
UploadCloudWidgetComponent
|
||||
],
|
||||
exports: [
|
||||
FormCloudComponent, UploadCloudWidgetComponent
|
||||
FormCloudComponent, UploadCloudWidgetComponent, TaskFormCloudComponent
|
||||
]
|
||||
})
|
||||
export class FormCloudModule { }
|
||||
|
@ -28,6 +28,10 @@ export class FormCloud {
|
||||
static COMPLETE_OUTCOME: string = '$complete';
|
||||
static START_PROCESS_OUTCOME: string = '$startProcess';
|
||||
|
||||
static CANCEL_OUTCOME: string = '$cancel';
|
||||
static CLAIM_OUTCOME: string = '$claim';
|
||||
static UNCLAIM_OUTCOME: string = '$unclaim';
|
||||
|
||||
readonly id: string;
|
||||
nodeId: string;
|
||||
readonly name: string;
|
||||
|
@ -19,4 +19,5 @@ export * from './models/form-cloud.model';
|
||||
export * from './models/task-variable-cloud.model';
|
||||
export * from './components/form-cloud.component';
|
||||
export * from './components/upload-cloud.widget';
|
||||
export * from './components/task-form-cloud.component';
|
||||
export * from './services/form-cloud.service';
|
||||
|
@ -228,5 +228,18 @@
|
||||
"PARENT_ID": "Parent Id",
|
||||
"NONE": "None"
|
||||
}
|
||||
},
|
||||
"ADF_CLOUD_TASK_FORM": {
|
||||
"EMPTY_FORM": {
|
||||
"TITLE": "No form available",
|
||||
"SUBTITLE": "Attach a form that can be viewed later",
|
||||
"BUTTONS": {
|
||||
"COMPLETE": "COMPLETE",
|
||||
"CANCEL": "CANCEL",
|
||||
"CLAIM": "CLAIM",
|
||||
"UNCLAIM": "UNCLAIM"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -7,6 +7,7 @@
|
||||
@import './../process/process-filters/components/edit-process-filter-cloud.component.scss';
|
||||
@import './../task/start-task/components/people-cloud/people-cloud.component.scss';
|
||||
@import './../group/components/group-cloud.component';
|
||||
@import './../form/components/task-form-cloud.component';
|
||||
|
||||
|
||||
@mixin adf-process-services-cloud-theme($theme) {
|
||||
@ -19,4 +20,5 @@
|
||||
@include adf-start-task-cloud-theme($theme);
|
||||
@include adf-cloud-people-theme($theme);
|
||||
@include adf-cloud-group-theme($theme);
|
||||
@include adf-task-form-cloud-theme($theme);
|
||||
}
|
||||
|
@ -71,7 +71,7 @@ export class TaskCloudService {
|
||||
*/
|
||||
canCompleteTask(taskDetails: TaskDetailsCloudModel): boolean {
|
||||
const currentUser = this.identityUserService.getCurrentUserInfo().username;
|
||||
return taskDetails.assignee && taskDetails.assignee === currentUser && taskDetails.isAssigned();
|
||||
return taskDetails && taskDetails.assignee && taskDetails.assignee === currentUser && taskDetails.isAssigned();
|
||||
}
|
||||
|
||||
/**
|
||||
@ -90,7 +90,7 @@ export class TaskCloudService {
|
||||
*/
|
||||
canUnclaimTask(taskDetails: TaskDetailsCloudModel): boolean {
|
||||
const currentUser = this.identityUserService.getCurrentUserInfo().username;
|
||||
return taskDetails.canUnclaimTask(currentUser);
|
||||
return taskDetails && taskDetails.canUnclaimTask(currentUser);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -75,11 +75,11 @@ export class TaskDetailsCloudModel {
|
||||
}
|
||||
|
||||
isCompleted(): boolean {
|
||||
return this.status && this.status === TaskStatusEnum.COMPLETED;
|
||||
return this.status === TaskStatusEnum.COMPLETED;
|
||||
}
|
||||
|
||||
isAssigned(): boolean {
|
||||
return this.status && this.status === TaskStatusEnum.ASSIGNED;
|
||||
return this.status === TaskStatusEnum.ASSIGNED;
|
||||
}
|
||||
|
||||
canClaimTask(): boolean {
|
||||
@ -87,7 +87,7 @@ export class TaskDetailsCloudModel {
|
||||
}
|
||||
|
||||
canUnclaimTask(user: string): boolean {
|
||||
return this.status !== TaskStatusEnum.COMPLETED && this.assignee === user;
|
||||
return this.isAssigned() && this.assignee === user;
|
||||
}
|
||||
}
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user