[ADF-3876] StartTaskCloud - Be able to start a task with a form (#4590)

* [ADF-3876] Added form cloud model

* [ADF-3876] Added service to get forms

* [ADF-3876] Added form selection to start task

* [ADF-3876] Added tests

* [ADF-3876] StartTaskCloud - Be able to start a task with a form

* [ADF-3876] StartTaskCloud - Be able to start a task with a form

* [ADF-3876] StartTaskCloud - Be able to start a task with a form

* [ADF-3876] Added form cloud model

* [ADF-3876] Added service to get forms

* [ADF-3876] Added form selection to start task

* [ADF-3876] Added tests

* [ADF-3876] StartTaskCloud - Be able to start a task with a form

* [ADF-3876] StartTaskCloud - changed name to component

* [ADF-3876] StartTaskCloud - Renamed component

* Rename form-selector-cloud.component.md to form-definition-selector-cloud.component.md

* [ADF-3876] Improve and clean code and fix service

* [ADF-3876] Fix unit test

* Update app.module.ts

* fix module

* move components in the right folders

* fix e2e task list
This commit is contained in:
arditdomi
2019-04-23 17:55:24 +01:00
committed by Eugenio Romano
parent 11688f5778
commit b371929170
29 changed files with 512 additions and 115 deletions
@@ -0,0 +1,27 @@
# [Form Definition Selector Cloud](../../../lib/process-services-cloud/src/lib/form-definition-selector/components/form-definition-selector-cloud.component.ts "Defined in form-definition-selector-cloud.component.ts")
Allows one form to be selected.
## Basic Usage
```html
<adf-cloud-form-definition-selector
[appName]="'simple-app'"
(selectForm)="onFormSelect($event)">
</adf-cloud-form-definition-selector>
```
## Class members
### Properties
| Name | Type | Default value | Description |
| ---- | ---- | ------------- | ----------- |
| appName | `string` | | (**required**) Name of the application. If specified, this shows the users who have access to the app.
### Events
| Name | Type | Description |
| ---- | ---- | ----------- |
| selectForm | [`EventEmitter`](https://angular.io/api/core/EventEmitter)`<`[`string`](../../../lib/core/userinfo/models/identity-user.model.ts)`>` | Emitted when a form is selected. |
@@ -22,7 +22,6 @@ import { NavigationBarPage } from '../pages/adf/navigationBarPage';
import { TasksCloudDemoPage } from '../pages/adf/demo-shell/process-services/tasksCloudDemoPage'; import { TasksCloudDemoPage } from '../pages/adf/demo-shell/process-services/tasksCloudDemoPage';
import { AppListCloudPage } from '@alfresco/adf-testing'; import { AppListCloudPage } from '@alfresco/adf-testing';
import { StringUtil } from '@alfresco/adf-testing'; import { StringUtil } from '@alfresco/adf-testing';
import { browser } from 'protractor';
import resources = require('../util/resources'); import resources = require('../util/resources');
describe('Task list cloud - selection', () => { describe('Task list cloud - selection', () => {
@@ -58,6 +57,10 @@ describe('Task list cloud - selection', () => {
tasks.push(response.entry.name); tasks.push(response.entry.name);
} }
done();
});
beforeEach(async (done) => {
navigationBarPage.navigateToProcessServicesCloudPage(); navigationBarPage.navigateToProcessServicesCloudPage();
appListCloudComponent.checkApsContainer(); appListCloudComponent.checkApsContainer();
appListCloudComponent.goToApp(simpleApp); appListCloudComponent.goToApp(simpleApp);
@@ -67,12 +70,6 @@ describe('Task list cloud - selection', () => {
done(); done();
}); });
afterEach(async (done) => {
await browser.refresh();
tasksCloudDemoPage.taskListCloudComponent().getDataTable().waitForTableBody();
done();
});
it('[C291914] Should not be able to select any row when selection mode is set to None', () => { it('[C291914] Should not be able to select any row when selection mode is set to None', () => {
tasksCloudDemoPage.clickSettingsButton().selectSelectionMode('None'); tasksCloudDemoPage.clickSettingsButton().selectSelectionMode('None');
tasksCloudDemoPage.clickSettingsButton().disableDisplayTaskDetails(); tasksCloudDemoPage.clickSettingsButton().disableDisplayTaskDetails();
@@ -107,6 +104,7 @@ describe('Task list cloud - selection', () => {
tasksCloudDemoPage.clickAppButton(); tasksCloudDemoPage.clickAppButton();
tasksCloudDemoPage.taskListCloudComponent().getDataTable().waitForTableBody(); tasksCloudDemoPage.taskListCloudComponent().getDataTable().waitForTableBody();
tasksCloudDemoPage.taskListCloudComponent().checkContentIsDisplayedByName(tasks[0]); tasksCloudDemoPage.taskListCloudComponent().checkContentIsDisplayedByName(tasks[0]);
tasksCloudDemoPage.taskListCloudComponent().selectRow(tasks[0]); tasksCloudDemoPage.taskListCloudComponent().selectRow(tasks[0]);
tasksCloudDemoPage.taskListCloudComponent().checkRowIsSelected(tasks[0]); tasksCloudDemoPage.taskListCloudComponent().checkRowIsSelected(tasks[0]);
@@ -0,0 +1,7 @@
<mat-form-field class="adf-form-definition-selector">
<mat-label>{{'ADF_CLOUD_TASK_LIST.START_TASK.FORM.LABEL.FORM'|translate}}</mat-label>
<mat-select class="adf-form-selector-dropdown" (selectionChange)="onSelect($event)">
<mat-option [value]="''">{{'ADF_CLOUD_TASK_LIST.START_TASK.FORM.LABEL.NONE'|translate}}</mat-option>
<mat-option *ngFor="let form of forms$ | async" [value]="form.id">{{ form.name }}</mat-option>
</mat-select>
</mat-form-field>
@@ -0,0 +1,5 @@
.adf {
&-form-definition-selector {
width: 100%;
}
}
@@ -0,0 +1,96 @@
/*!
* @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 { ComponentFixture, TestBed } from '@angular/core/testing';
import { AlfrescoApiService, AppConfigService, LogService, setupTestBed, StorageService, UserPreferencesService } from '@alfresco/adf-core';
import { ProcessServiceCloudTestingModule } from '../../testing/process-service-cloud.testing.module';
import { StartTaskCloudTestingModule } from '../../task/start-task/testing/start-task-cloud.testing.module';
import { CUSTOM_ELEMENTS_SCHEMA } from '@angular/core';
import { FormDefinitionSelectorCloudComponent } from './form-definition-selector-cloud.component';
import { By } from '@angular/platform-browser';
import { of } from 'rxjs';
import { FormDefinitionSelectorCloudService } from '../services/form-definition-selector-cloud.service';
describe('FormDefinitionCloudComponent', () => {
let fixture: ComponentFixture<FormDefinitionSelectorCloudComponent>;
let service: FormDefinitionSelectorCloudService;
let element: HTMLElement;
let getFormsSpy: jasmine.Spy;
setupTestBed({
imports: [ProcessServiceCloudTestingModule, StartTaskCloudTestingModule],
providers: [FormDefinitionSelectorCloudService, AlfrescoApiService, AppConfigService, LogService, StorageService, UserPreferencesService],
schemas: [CUSTOM_ELEMENTS_SCHEMA]
});
beforeEach(() => {
fixture = TestBed.createComponent(FormDefinitionSelectorCloudComponent);
element = fixture.nativeElement;
service = TestBed.get(FormDefinitionSelectorCloudService);
getFormsSpy = spyOn(service, 'getForms').and.returnValue(of([{ id: 'fake-form', name: 'fakeForm' }]));
});
it('should load the forms by default', () => {
fixture.detectChanges();
fixture.whenStable().then(() => {
fixture.detectChanges();
const clickMatSelect = fixture.debugElement.query(By.css(('.mat-select-trigger')));
clickMatSelect.triggerEventHandler('click', null);
fixture.detectChanges();
const options: any = fixture.debugElement.queryAll(By.css('mat-option'));
expect(options[0].nativeElement.innerText.trim()).toBe('ADF_CLOUD_TASK_LIST.START_TASK.FORM.LABEL.NONE');
expect(options[1].nativeElement.innerText.trim()).toBe('fakeForm');
expect(getFormsSpy).toHaveBeenCalled();
});
});
it('should load only None option when no forms exist', () => {
getFormsSpy.and.returnValue(of([]));
fixture.detectChanges();
fixture.whenStable().then(() => {
fixture.detectChanges();
const clickMatSelect = fixture.debugElement.query(By.css(('.mat-select-trigger')));
clickMatSelect.triggerEventHandler('click', null);
fixture.detectChanges();
const options: any = fixture.debugElement.queryAll(By.css('mat-option'));
expect((options).length).toBe(1);
});
});
it('should not preselect any form by default', () => {
fixture.detectChanges();
const formInput = element.querySelector('mat-select');
expect(formInput).toBeDefined();
expect(formInput.nodeValue).toBeNull();
});
it('should display the name of the form that is selected', () => {
fixture.detectChanges();
fixture.whenStable().then(() => {
const clickMatSelect = fixture.debugElement.query(By.css(('.mat-select-trigger')));
clickMatSelect.triggerEventHandler('click', null);
fixture.detectChanges();
const options: any = fixture.debugElement.queryAll(By.css('mat-option'));
options[1].triggerEventHandler('click', {});
fixture.detectChanges();
const selected = fixture.debugElement.query(By.css('mat-select'));
const selectedValue = ((selected).nativeElement.innerText);
expect(selectedValue.trim()).toBe('fakeForm');
});
});
});
@@ -0,0 +1,53 @@
/*!
* @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, OnInit, Output } from '@angular/core';
import { Observable } from 'rxjs';
import { FormDefinitionSelectorCloudService } from '../services/form-definition-selector-cloud.service';
import { FormDefinitionSelectorCloudModel } from '../models/form-definition-selector-cloud.model';
import { MatSelectChange } from '@angular/material';
@Component({
selector: 'adf-cloud-form-definition-selector',
templateUrl: './form-definition-selector-cloud.component.html',
styleUrls: ['./form-definition-selector-cloud.component.scss']
})
export class FormDefinitionSelectorCloudComponent implements OnInit {
/** Name of the application. If specified, this shows the users who have access to the app. */
@Input()
appName: string;
/** Emitted when a form is selected. */
@Output()
selectForm: EventEmitter<string> = new EventEmitter<string>();
forms$: Observable<FormDefinitionSelectorCloudModel[]>;
constructor(private formDefinitionCloudService: FormDefinitionSelectorCloudService) {
}
ngOnInit(): void {
this.forms$ = this.formDefinitionCloudService.getForms(this.appName);
}
onSelect(event: MatSelectChange) {
this.selectForm.emit(event.value);
}
}
@@ -20,31 +20,32 @@ import { CommonModule } from '@angular/common';
import { FlexLayoutModule } from '@angular/flex-layout'; import { FlexLayoutModule } from '@angular/flex-layout';
import { TemplateModule, FormBaseModule, PipeModule, CoreModule } from '@alfresco/adf-core'; import { TemplateModule, FormBaseModule, PipeModule, CoreModule } from '@alfresco/adf-core';
import { FormsModule, ReactiveFormsModule } from '@angular/forms'; import { FormsModule, ReactiveFormsModule } from '@angular/forms';
import { FormCloudComponent } from './components/form-cloud.component';
import { UploadCloudWidgetComponent } from './components/upload-cloud.widget'; import { UploadCloudWidgetComponent } from './components/upload-cloud.widget';
import { MaterialModule } from '../material.module'; import { MaterialModule } from '../material.module';
import { TaskFormCloudComponent } from './components/task-form-cloud.component'; import { FormCloudComponent } from './components/form-cloud.component';
import { TaskCloudModule } from '../task/task-cloud.module'; import { FormDefinitionSelectorCloudComponent } from './components/form-definition-selector-cloud.component';
import { FormDefinitionSelectorCloudService } from './services/form-definition-selector-cloud.service';
@NgModule({ @NgModule({
imports: [ imports: [
CommonModule, CommonModule,
PipeModule, PipeModule,
TemplateModule, TemplateModule,
FlexLayoutModule, FlexLayoutModule,
MaterialModule, MaterialModule,
FormsModule, FormsModule,
ReactiveFormsModule, ReactiveFormsModule,
FormBaseModule, FormBaseModule,
CoreModule, CoreModule
TaskCloudModule
], ],
declarations: [FormCloudComponent, UploadCloudWidgetComponent, TaskFormCloudComponent], declarations: [FormCloudComponent, UploadCloudWidgetComponent, FormDefinitionSelectorCloudComponent],
providers: [FormDefinitionSelectorCloudService],
entryComponents: [ entryComponents: [
UploadCloudWidgetComponent UploadCloudWidgetComponent
], ],
exports: [ exports: [
FormCloudComponent, UploadCloudWidgetComponent, TaskFormCloudComponent FormCloudComponent, UploadCloudWidgetComponent, FormDefinitionSelectorCloudComponent
] ]
}) })
export class FormCloudModule { } export class FormCloudModule {
}
@@ -0,0 +1,33 @@
/*!
* @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.
*/
export class FormDefinitionSelectorCloudModel {
id: number;
name: string;
description: string;
version: string;
constructor(obj?: any) {
if (obj) {
this.id = obj.id || null;
this.name = obj.name || null;
this.description = obj.description || null;
this.version = obj.version || null;
}
}
}
@@ -17,7 +17,11 @@
export * from './models/form-cloud.model'; export * from './models/form-cloud.model';
export * from './models/task-variable-cloud.model'; export * from './models/task-variable-cloud.model';
export * from './models/form-definition-selector-cloud.model';
export * from './components/form-cloud.component'; export * from './components/form-cloud.component';
export * from './components/upload-cloud.widget'; export * from './components/upload-cloud.widget';
export * from './components/task-form-cloud.component'; export * from './components/form-definition-selector-cloud.component';
export * from './services/form-cloud.service'; export * from './services/form-cloud.service';
export * from './services/form-definition-selector-cloud.service';
@@ -0,0 +1,75 @@
/*!
* @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 { Injectable } from '@angular/core';
import { AlfrescoApiService, AppConfigService, LogService } from '@alfresco/adf-core';
import { catchError, map } from 'rxjs/operators';
import { FormDefinitionSelectorCloudModel } from '../models/form-definition-selector-cloud.model';
import { from, Observable, throwError } from 'rxjs';
@Injectable({
providedIn: 'root'
})
export class FormDefinitionSelectorCloudService {
contextRoot: string;
contentTypes = ['application/json'];
accepts = ['application/json'];
returnType = Object;
constructor(private apiService: AlfrescoApiService,
private appConfigService: AppConfigService,
private logService: LogService) {
this.contextRoot = this.appConfigService.get('bpmHost', '');
}
/**
* Get all forms of an app.
* @param appName Name of the application
* @returns Details of the forms
*/
getForms(appName: string): Observable<FormDefinitionSelectorCloudModel[]> {
const queryUrl = this.buildGetFormsUrl(appName);
const bodyParam = {}, pathParams = {}, queryParams = {}, headerParams = {},
formParams = {}, contentTypes = ['application/json'], accepts = ['application/json'];
return from(
this.apiService
.getInstance()
.oauth2Auth.callCustomApi(
queryUrl, 'GET', pathParams, queryParams,
headerParams, formParams, bodyParam,
contentTypes, accepts, null, null)
).pipe(
map((data: any) => {
return data.map((formData: any) => {
return <FormDefinitionSelectorCloudModel> formData.formRepresentation;
});
}),
catchError((err) => this.handleError(err))
);
}
private buildGetFormsUrl(appName: string): any {
return `${this.appConfigService.get('bpmHost')}/${appName}/form/v1/forms`;
}
private handleError(error: any) {
this.logService.error(error);
return throwError(error || 'Server error');
}
}
@@ -66,7 +66,8 @@
"ASSIGNEE": "Assignee", "ASSIGNEE": "Assignee",
"CANDIDATE_GROUP": "Candidate Group", "CANDIDATE_GROUP": "Candidate Group",
"FORM": "Form", "FORM": "Form",
"DATE": "Choose Date" "DATE": "Choose Date",
"NONE": "None"
}, },
"ACTION": { "ACTION": {
"START": "Start", "START": "Start",
@@ -22,6 +22,7 @@ import { TaskCloudModule } from './task/task-cloud.module';
import { ProcessCloudModule } from './process/process-cloud.module'; import { ProcessCloudModule } from './process/process-cloud.module';
import { GroupCloudModule } from './group/group-cloud.module'; import { GroupCloudModule } from './group/group-cloud.module';
import { FormCloudModule } from './form/form-cloud.module'; import { FormCloudModule } from './form/form-cloud.module';
import { TaskFormModule } from './task/task-form/task-form.module';
@NgModule({ @NgModule({
imports: [ imports: [
@@ -30,7 +31,8 @@ import { FormCloudModule } from './form/form-cloud.module';
ProcessCloudModule, ProcessCloudModule,
TaskCloudModule, TaskCloudModule,
GroupCloudModule, GroupCloudModule,
FormCloudModule FormCloudModule,
TaskFormModule
], ],
providers: [ providers: [
{ {
@@ -47,7 +49,8 @@ import { FormCloudModule } from './form/form-cloud.module';
ProcessCloudModule, ProcessCloudModule,
TaskCloudModule, TaskCloudModule,
GroupCloudModule, GroupCloudModule,
FormCloudModule FormCloudModule,
TaskFormModule
] ]
}) })
export class ProcessServicesCloudModule { } export class ProcessServicesCloudModule { }
@@ -1,13 +1,13 @@
@import './../app/components/app-details-cloud.component'; @import './../app/components/app-details-cloud.component';
@import './../app/components/app-list-cloud.component'; @import './../app/components/app-list-cloud.component';
@import './../task/task-filters/components/task-filters-cloud.component.scss';
@import './../task/task-filters/components/edit-task-filter-cloud.component.scss';
@import './../process/process-list/components/process-list-cloud.component.scss';
@import './../task/start-task/components/start-task-cloud.component.scss';
@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 './../group/components/group-cloud.component';
@import './../form/components/task-form-cloud.component'; @import './../process/process-list/components/process-list-cloud.component.scss';
@import './../process/process-filters/components/edit-process-filter-cloud.component.scss';
@import './../task/task-form/components/task-form-cloud.component';
@import './../task/start-task/components/people-cloud/people-cloud.component.scss';
@import './../task/start-task/components/start-task-cloud.component.scss';
@import './../task/task-filters/components/edit-task-filter-cloud.component.scss';
@import './../task/task-filters/components/task-filters-cloud.component.scss';
@mixin adf-process-services-cloud-theme($theme) { @mixin adf-process-services-cloud-theme($theme) {
@@ -0,0 +1,22 @@
/*!
* @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.
*/
export * from './claim-task.directive';
export * from './unclaim-task.directive';
export * from './complete-task.directive';
export * from './task-directive.module';
@@ -19,7 +19,9 @@ export * from './task-list/public-api';
export * from './task-filters/public-api'; export * from './task-filters/public-api';
export * from './start-task/public-api'; export * from './start-task/public-api';
export * from './task-header/public-api'; export * from './task-header/public-api';
export * from './task-form/public-api';
export * from './directives/public-api';
export * from './services/task-cloud.service';
export * from './task-cloud.module'; export * from './task-cloud.module';
export * from './directives/task-directive.module';
export * from './services/task-cloud.service';
@@ -1,10 +1,11 @@
<mat-card> <mat-card>
<mat-card-header fxLayout="row" fxLayoutAlign="start center" fxLayoutGap="10px" class="adf-cloud-start-task-heading"> <mat-card-header fxLayout="row" fxLayoutAlign="start center" fxLayoutGap="10px"
class="adf-cloud-start-task-heading">
<mat-card-title>{{'ADF_CLOUD_TASK_LIST.START_TASK.FORM.TITLE' | translate}}</mat-card-title> <mat-card-title>{{'ADF_CLOUD_TASK_LIST.START_TASK.FORM.TITLE' | translate}}</mat-card-title>
</mat-card-header> </mat-card-header>
<form [formGroup]="taskForm" fxLayout="column" (ngSubmit)="saveTask()"> <form [formGroup]="taskForm" fxLayout="column" (ngSubmit)="saveTask()">
<mat-card-content> <mat-card-content>
<div class="adf-task-name"> <div class="adf-task-name">
<mat-form-field fxFlex> <mat-form-field fxFlex>
<mat-label>{{'ADF_CLOUD_TASK_LIST.START_TASK.FORM.LABEL.NAME' | translate }}</mat-label> <mat-label>{{'ADF_CLOUD_TASK_LIST.START_TASK.FORM.LABEL.NAME' | translate }}</mat-label>
@@ -17,7 +18,7 @@
{{ 'ADF_CLOUD_START_TASK.ERROR.REQUIRED' | translate }} {{ 'ADF_CLOUD_START_TASK.ERROR.REQUIRED' | translate }}
</mat-error> </mat-error>
<mat-error *ngIf="nameController.hasError('maxlength')"> <mat-error *ngIf="nameController.hasError('maxlength')">
{{ 'ADF_CLOUD_START_TASK.ERROR.MAXIMUM_LENGTH' | translate : { characters : maxNameLength } }} {{ 'ADF_CLOUD_START_TASK.ERROR.MAXIMUM_LENGTH' | translate : {characters: maxNameLength} }}
</mat-error> </mat-error>
</mat-form-field> </mat-form-field>
</div> </div>
@@ -34,25 +35,25 @@
<mat-form-field fxFlex> <mat-form-field fxFlex>
<div style="height: 40px;"> <div style="height: 40px;">
<input matInput type="number" placeholder="Priority" formControlName="priority"> <input matInput type="number" placeholder="Priority" formControlName="priority">
</div> </div>
</mat-form-field> </mat-form-field>
</div> </div>
<div fxLayout="row" fxLayout.lt-md="column" fxLayoutGap="20px" fxLayoutGap.lt-md="0px"> <div fxLayout="row" fxLayout.lt-md="column" fxLayoutGap="20px" fxLayoutGap.lt-md="0px">
<mat-form-field fxFlex> <mat-form-field fxFlex>
<input matInput <input matInput
[matDatepicker]="taskDatePicker" [matDatepicker]="taskDatePicker"
(keydown)="true" (keydown)="true"
(focusout)="onDateChanged($event.srcElement.value)" (focusout)="onDateChanged($event.srcElement.value)"
placeholder="{{'ADF_CLOUD_TASK_LIST.START_TASK.FORM.LABEL.DATE'|translate}}" placeholder="{{'ADF_CLOUD_TASK_LIST.START_TASK.FORM.LABEL.DATE'|translate}}"
[(ngModel)]="dueDate" [(ngModel)]="dueDate"
[ngModelOptions]="{standalone: true}" [ngModelOptions]="{standalone: true}"
id="date_id"> id="date_id">
<mat-datepicker-toggle matSuffix [for]="taskDatePicker"></mat-datepicker-toggle> <mat-datepicker-toggle matSuffix [for]="taskDatePicker"></mat-datepicker-toggle>
<mat-datepicker #taskDatePicker <mat-datepicker #taskDatePicker
[touchUi]="true" [touchUi]="true"
(dateChanged)="onDateChanged($event)"> (dateChanged)="onDateChanged($event)">
</mat-datepicker> </mat-datepicker>
<div class="adf-cloud-date-error-container"> <div class="adf-cloud-date-error-container">
<div *ngIf="dateError"> <div *ngIf="dateError">
<div class="adf-error-text">{{'ADF_CLOUD_START_TASK.ERROR.DATE' | translate}}</div> <div class="adf-error-text">{{'ADF_CLOUD_START_TASK.ERROR.DATE' | translate}}</div>
@@ -61,41 +62,46 @@
</div> </div>
</mat-form-field> </mat-form-field>
<adf-cloud-people fxFlex #peopleInput *ngIf="currentUser" <adf-cloud-people fxFlex #peopleInput *ngIf="currentUser"
[appName]="appName" [appName]="appName"
[preSelectUsers]="[currentUser]" [preSelectUsers]="[currentUser]"
(selectUser)="onAssigneeSelect($event)" (selectUser)="onAssigneeSelect($event)"
[title]="'ADF_TASK_LIST.START_TASK.FORM.LABEL.ASSIGNEE'" [title]="'ADF_TASK_LIST.START_TASK.FORM.LABEL.ASSIGNEE'"
(removeUser)="onAssigneeRemove()"></adf-cloud-people> (removeUser)="onAssigneeRemove()"></adf-cloud-people>
</div> </div>
<div class="input-row" fxLayout="row" fxLayout.lt-md="column" fxLayoutGap="20px" fxLayoutGap.lt-md="0px"> <div fxLayout="row" fxLayout.lt-md="column" fxLayoutGap="20px" fxLayoutGap.lt-md="0px">
<adf-cloud-group fxFlex #groupInput *ngIf="currentUser" <adf-cloud-group fxFlex #groupInput *ngIf="currentUser"
[mode]="'multiple'" [mode]="'multiple'"
[title]="'ADF_CLOUD_TASK_LIST.START_TASK.FORM.LABEL.CANDIDATE_GROUP'" [title]="'ADF_CLOUD_TASK_LIST.START_TASK.FORM.LABEL.CANDIDATE_GROUP'"
[appName]="appName" [appName]="appName"
(selectGroup)="onCandiateGroupSelect($event)" (selectGroup)="onCandidateGroupSelect($event)"
(removeGroup)="onCandiateGroupRemove($event)"></adf-cloud-group> (removeGroup)="onCandidateGroupRemove($event)">
<div fxFlex></div> </adf-cloud-group>
<adf-cloud-form-definition-selector fxFlex
[appName]="appName"
(selectForm)="onFormSelect($event)">
</adf-cloud-form-definition-selector>
</div> </div>
</mat-card-content> </mat-card-content>
<mat-card-actions> <mat-card-actions>
<div class="adf-cloud-start-task-footer" fxLayout="row" fxLayoutAlign="end end" > <div class="adf-cloud-start-task-footer" fxLayout="row" fxLayoutAlign="end end">
<button <button
mat-button mat-button
type="button" type="button"
(click)="onCancel()" (click)="onCancel()"
id="button-cancel"> id="button-cancel">
{{'ADF_CLOUD_TASK_LIST.START_TASK.FORM.ACTION.CANCEL'|translate}} {{'ADF_CLOUD_TASK_LIST.START_TASK.FORM.ACTION.CANCEL'|translate}}
</button> </button>
<button <button
color="primary" color="primary"
type="submit" [disabled]="dateError || !taskForm.valid || submitted || assignee.hasError() || candidateGroups.hasError()" type="submit"
mat-button [disabled]="dateError || !taskForm.valid || submitted || assignee.hasError() || candidateGroups.hasError()"
id="button-start"> mat-button
{{'ADF_CLOUD_TASK_LIST.START_TASK.FORM.ACTION.START'|translate}} id="button-start">
</button> {{'ADF_CLOUD_TASK_LIST.START_TASK.FORM.ACTION.START'|translate}}
</div> </button>
</mat-card-actions> </div>
</mat-card-actions>
</form> </form>
</mat-card> </mat-card>
@@ -33,6 +33,7 @@ import { CUSTOM_ELEMENTS_SCHEMA } from '@angular/core';
import { ProcessServiceCloudTestingModule } from './../../../testing/process-service-cloud.testing.module'; import { ProcessServiceCloudTestingModule } from './../../../testing/process-service-cloud.testing.module';
import { StartTaskCloudTestingModule } from '../testing/start-task-cloud.testing.module'; import { StartTaskCloudTestingModule } from '../testing/start-task-cloud.testing.module';
import { TaskDetailsCloudModel } from '../models/task-details-cloud.model'; import { TaskDetailsCloudModel } from '../models/task-details-cloud.model';
import { FormDefinitionSelectorCloudService } from '../../../form/services/form-definition-selector-cloud.service';
describe('StartTaskCloudComponent', () => { describe('StartTaskCloudComponent', () => {
@@ -40,12 +41,21 @@ describe('StartTaskCloudComponent', () => {
let fixture: ComponentFixture<StartTaskCloudComponent>; let fixture: ComponentFixture<StartTaskCloudComponent>;
let service: StartTaskCloudService; let service: StartTaskCloudService;
let identityService: IdentityUserService; let identityService: IdentityUserService;
let formDefinitionSelectorCloudService: FormDefinitionSelectorCloudService;
let element: HTMLElement; let element: HTMLElement;
let createNewTaskSpy: jasmine.Spy; let createNewTaskSpy: jasmine.Spy;
setupTestBed({ setupTestBed({
imports: [ProcessServiceCloudTestingModule, StartTaskCloudTestingModule], imports: [ProcessServiceCloudTestingModule, StartTaskCloudTestingModule],
providers: [StartTaskCloudService, AlfrescoApiService, AppConfigService, LogService, StorageService, UserPreferencesService], providers: [
StartTaskCloudService,
AlfrescoApiService,
AppConfigService,
LogService,
StorageService,
UserPreferencesService,
FormDefinitionSelectorCloudService
],
schemas: [ CUSTOM_ELEMENTS_SCHEMA ] schemas: [ CUSTOM_ELEMENTS_SCHEMA ]
}); });
@@ -56,8 +66,10 @@ describe('StartTaskCloudComponent', () => {
service = TestBed.get(StartTaskCloudService); service = TestBed.get(StartTaskCloudService);
identityService = TestBed.get(IdentityUserService); identityService = TestBed.get(IdentityUserService);
formDefinitionSelectorCloudService = TestBed.get(FormDefinitionSelectorCloudService);
createNewTaskSpy = spyOn(service, 'createNewTask').and.returnValue(of(taskDetailsMock)); createNewTaskSpy = spyOn(service, 'createNewTask').and.returnValue(of(taskDetailsMock));
spyOn(identityService, 'getCurrentUserInfo').and.returnValue(new IdentityUserModel({username: 'currentUser', firstName: 'Test', lastName: 'User'})); spyOn(identityService, 'getCurrentUserInfo').and.returnValue(new IdentityUserModel({username: 'currentUser', firstName: 'Test', lastName: 'User'}));
spyOn(formDefinitionSelectorCloudService, 'getForms').and.returnValue(of([]));
fixture.detectChanges(); fixture.detectChanges();
})); }));
@@ -97,6 +97,8 @@ export class StartTaskCloudComponent implements OnInit, OnDestroy {
currentUser: IdentityUserModel; currentUser: IdentityUserModel;
formKey: string;
private localeSub: Subscription; private localeSub: Subscription;
private createTaskSub: Subscription; private createTaskSub: Subscription;
@@ -112,7 +114,6 @@ export class StartTaskCloudComponent implements OnInit, OnDestroy {
this.userPreferencesService.select(UserPreferenceValues.Locale).subscribe((locale) => { this.userPreferencesService.select(UserPreferenceValues.Locale).subscribe((locale) => {
this.dateAdapter.setLocale(locale); this.dateAdapter.setLocale(locale);
}); });
this.loadCurrentUser(); this.loadCurrentUser();
this.buildForm(); this.buildForm();
} }
@@ -131,7 +132,8 @@ export class StartTaskCloudComponent implements OnInit, OnDestroy {
this.taskForm = this.formBuilder.group({ this.taskForm = this.formBuilder.group({
name: new FormControl(this.name, [Validators.required, Validators.maxLength(this.getMaxNameLength()), this.whitespaceValidator]), name: new FormControl(this.name, [Validators.required, Validators.maxLength(this.getMaxNameLength()), this.whitespaceValidator]),
priority: new FormControl(), priority: new FormControl(),
description: new FormControl('', [this.whitespaceValidator]) description: new FormControl('', [this.whitespaceValidator]),
formKey: new FormControl()
}); });
} }
@@ -151,7 +153,9 @@ export class StartTaskCloudComponent implements OnInit, OnDestroy {
newTask.appName = this.appName; newTask.appName = this.appName;
newTask.dueDate = this.dueDate; newTask.dueDate = this.dueDate;
newTask.assignee = this.assigneeName; newTask.assignee = this.assigneeName;
newTask.formKey = this.formKey;
newTask.candidateGroups = this.candidateGroupNames; newTask.candidateGroups = this.candidateGroupNames;
this.createNewTask(new TaskDetailsCloudModel(newTask)); this.createNewTask(new TaskDetailsCloudModel(newTask));
} }
@@ -192,15 +196,17 @@ export class StartTaskCloudComponent implements OnInit, OnDestroy {
this.assigneeName = ''; this.assigneeName = '';
} }
onCandiateGroupSelect(candidateGroup: any) { onCandidateGroupSelect(candidateGroup: any) {
if (candidateGroup.name) { if (candidateGroup.name) {
this.candidateGroupNames.push(candidateGroup.name); this.candidateGroupNames.push(candidateGroup.name);
} }
} }
onCandiateGroupRemove(candidateGroup: any) { onCandidateGroupRemove(candidateGroup: any) {
if (candidateGroup.name) { if (candidateGroup.name) {
this.candidateGroupNames = this.candidateGroupNames.filter((name: string) => { return name !== candidateGroup.name; }); this.candidateGroupNames = this.candidateGroupNames.filter((name: string) => {
return name !== candidateGroup.name;
});
} }
} }
@@ -217,4 +223,8 @@ export class StartTaskCloudComponent implements OnInit, OnDestroy {
get priorityController(): AbstractControl { get priorityController(): AbstractControl {
return this.taskForm.get('priority'); return this.taskForm.get('priority');
} }
onFormSelect(formKey: string) {
this.formKey = formKey || '';
}
} }
@@ -24,6 +24,7 @@ export class StartTaskCloudRequestModel {
candidateUsers: string[]; candidateUsers: string[];
candidateGroups: string[]; candidateGroups: string[];
payloadType: string; payloadType: string;
formKey: string;
constructor(obj?: any) { constructor(obj?: any) {
if (obj) { if (obj) {
@@ -34,6 +35,7 @@ export class StartTaskCloudRequestModel {
this.dueDate = obj.dueDate || null; this.dueDate = obj.dueDate || null;
this.candidateUsers = obj.candidateUsers || null; this.candidateUsers = obj.candidateUsers || null;
this.candidateGroups = obj.candidateGroups || null; this.candidateGroups = obj.candidateGroups || null;
this.formKey = obj.formKey || null;
this.payloadType = 'CreateTaskPayload'; this.payloadType = 'CreateTaskPayload';
} }
} }
@@ -25,27 +25,31 @@ import { StartTaskCloudService } from './services/start-task-cloud.service';
import { FormsModule, ReactiveFormsModule } from '@angular/forms'; import { FormsModule, ReactiveFormsModule } from '@angular/forms';
import { PeopleCloudComponent } from './components/people-cloud/people-cloud.component'; import { PeopleCloudComponent } from './components/people-cloud/people-cloud.component';
import { GroupCloudModule } from '../../group/group-cloud.module'; import { GroupCloudModule } from '../../group/group-cloud.module';
import { TaskCloudService } from '../services/task-cloud.service';
import { FormCloudModule } from '../../form/form-cloud.module';
@NgModule({ @NgModule({
imports: [ imports: [
CommonModule, CommonModule,
PipeModule, PipeModule,
TemplateModule, TemplateModule,
FlexLayoutModule, FlexLayoutModule,
MaterialModule, MaterialModule,
FormsModule, FormsModule,
ReactiveFormsModule, ReactiveFormsModule,
GroupCloudModule, GroupCloudModule,
GroupCloudModule, CoreModule,
CoreModule FormCloudModule
], ],
declarations: [StartTaskCloudComponent, PeopleCloudComponent], declarations: [StartTaskCloudComponent, PeopleCloudComponent],
providers: [ providers: [
StartTaskCloudService StartTaskCloudService,
], TaskCloudService
],
exports: [ exports: [
StartTaskCloudComponent, StartTaskCloudComponent,
PeopleCloudComponent PeopleCloudComponent
] ]
}) })
export class StartTaskCloudModule { } export class StartTaskCloudModule {
}
@@ -21,6 +21,7 @@ import { TaskFiltersCloudModule } from './task-filters/task-filters-cloud.module
import { StartTaskCloudModule } from './start-task/start-task-cloud.module'; import { StartTaskCloudModule } from './start-task/start-task-cloud.module';
import { TaskHeaderCloudModule } from './task-header/task-header-cloud.module'; import { TaskHeaderCloudModule } from './task-header/task-header-cloud.module';
import { TaskDirectiveModule } from './directives/task-directive.module'; import { TaskDirectiveModule } from './directives/task-directive.module';
import { TaskFormModule } from './task-form/task-form.module';
@NgModule({ @NgModule({
imports: [ imports: [
@@ -28,14 +29,16 @@ import { TaskDirectiveModule } from './directives/task-directive.module';
TaskFiltersCloudModule, TaskFiltersCloudModule,
StartTaskCloudModule, StartTaskCloudModule,
TaskHeaderCloudModule, TaskHeaderCloudModule,
TaskDirectiveModule TaskDirectiveModule,
TaskFormModule
], ],
exports: [ exports: [
TaskListCloudModule, TaskListCloudModule,
TaskFiltersCloudModule, TaskFiltersCloudModule,
StartTaskCloudModule, StartTaskCloudModule,
TaskHeaderCloudModule, TaskHeaderCloudModule,
TaskDirectiveModule TaskDirectiveModule,
TaskFormModule
] ]
}) })
export class TaskCloudModule { } export class TaskCloudModule { }
@@ -17,6 +17,9 @@
export * from './components/task-filters-cloud.component'; export * from './components/task-filters-cloud.component';
export * from './components/edit-task-filter-cloud.component'; export * from './components/edit-task-filter-cloud.component';
export * from './models/filter-cloud.model'; export * from './models/filter-cloud.model';
export * from './services/task-filter-cloud.service'; export * from './services/task-filter-cloud.service';
export * from './task-filters-cloud.module'; export * from './task-filters-cloud.module';
@@ -15,12 +15,14 @@
* limitations under the License. * limitations under the License.
*/ */
import { ProcessServiceCloudTestingModule } from '../../testing/process-service-cloud.testing.module'; import { ProcessServiceCloudTestingModule } from '../../../testing/process-service-cloud.testing.module';
import { FormCloudModule } from '../form-cloud.module'; import { TaskCloudModule } from '../../task-cloud.module';
import { TaskDirectiveModule } from '../../directives/task-directive.module';
import { ComponentFixture, TestBed, async } from '@angular/core/testing'; import { ComponentFixture, TestBed, async } from '@angular/core/testing';
import { TaskFormCloudComponent } from './task-form-cloud.component'; import { TaskFormCloudComponent } from './task-form-cloud.component';
import { setupTestBed, IdentityUserService } from '@alfresco/adf-core'; import { setupTestBed, IdentityUserService } from '@alfresco/adf-core';
import { TaskCloudService, TaskDetailsCloudModel } from '../../task/public-api'; import { TaskDetailsCloudModel } from '../../start-task/models/task-details-cloud.model';
import { TaskCloudService } from '../../services/task-cloud.service';
import { of } from 'rxjs'; import { of } from 'rxjs';
import { DebugElement, CUSTOM_ELEMENTS_SCHEMA, SimpleChange } from '@angular/core'; import { DebugElement, CUSTOM_ELEMENTS_SCHEMA, SimpleChange } from '@angular/core';
import { By } from '@angular/platform-browser'; import { By } from '@angular/platform-browser';
@@ -52,14 +54,14 @@ describe('TaskFormCloudComponent', () => {
let fixture: ComponentFixture<TaskFormCloudComponent>; let fixture: ComponentFixture<TaskFormCloudComponent>;
setupTestBed({ setupTestBed({
imports: [ProcessServiceCloudTestingModule, FormCloudModule], imports: [ProcessServiceCloudTestingModule, TaskCloudModule, TaskDirectiveModule],
schemas: [CUSTOM_ELEMENTS_SCHEMA] schemas: [CUSTOM_ELEMENTS_SCHEMA]
}); });
beforeEach(() => { beforeEach(() => {
taskDetails.status = 'ASSIGNED'; taskDetails.status = 'ASSIGNED';
identityUserService = TestBed.get(IdentityUserService); identityUserService = TestBed.get(IdentityUserService);
getCurrentUserSpy = spyOn(identityUserService, 'getCurrentUserInfo').and.returnValue({username: 'admin.adf'}); getCurrentUserSpy = spyOn(identityUserService, 'getCurrentUserInfo').and.returnValue({ username: 'admin.adf' });
taskCloudService = TestBed.get(TaskCloudService); taskCloudService = TestBed.get(TaskCloudService);
getTaskSpy = spyOn(taskCloudService, 'getTaskById').and.returnValue(of(new TaskDetailsCloudModel(taskDetails))); getTaskSpy = spyOn(taskCloudService, 'getTaskById').and.returnValue(of(new TaskDetailsCloudModel(taskDetails)));
@@ -74,6 +76,7 @@ describe('TaskFormCloudComponent', () => {
}); });
describe('Complete button', () => { describe('Complete button', () => {
it('should show complete button when status is ASSIGNED', async(() => { it('should show complete button when status is ASSIGNED', async(() => {
component.appName = 'app1'; component.appName = 'app1';
component.taskId = 'task1'; component.taskId = 'task1';
@@ -115,6 +118,7 @@ describe('TaskFormCloudComponent', () => {
}); });
describe('Claim/Unclaim buttons', () => { describe('Claim/Unclaim buttons', () => {
it('should show unclaim button when status is ASSIGNED', async(() => { it('should show unclaim button when status is ASSIGNED', async(() => {
component.appName = 'app1'; component.appName = 'app1';
component.taskId = 'task1'; component.taskId = 'task1';
@@ -185,6 +189,7 @@ describe('TaskFormCloudComponent', () => {
}); });
describe('Cancel button', () => { describe('Cancel button', () => {
it('should show cancel button by default', async(() => { it('should show cancel button by default', async(() => {
component.appName = 'app1'; component.appName = 'app1';
component.taskId = 'task1'; component.taskId = 'task1';
@@ -212,6 +217,7 @@ describe('TaskFormCloudComponent', () => {
}); });
describe('Inputs', () => { describe('Inputs', () => {
it('should not show complete/claim/unclaim buttons when readOnly=true', async(() => { it('should not show complete/claim/unclaim buttons when readOnly=true', async(() => {
component.appName = 'app1'; component.appName = 'app1';
component.taskId = 'task1'; component.taskId = 'task1';
@@ -258,6 +264,7 @@ describe('TaskFormCloudComponent', () => {
}); });
describe('Events', () => { describe('Events', () => {
it('should emit cancelClick when cancel button is clicked', (done) => { it('should emit cancelClick when cancel button is clicked', (done) => {
component.appName = 'app1'; component.appName = 'app1';
component.taskId = 'task1'; component.taskId = 'task1';
@@ -19,8 +19,9 @@ import {
Component, EventEmitter, Input, OnChanges, Component, EventEmitter, Input, OnChanges,
Output, SimpleChanges Output, SimpleChanges
} from '@angular/core'; } from '@angular/core';
import { FormCloud } from '../models/form-cloud.model'; import { FormCloud } from '../../../form/models/form-cloud.model';
import { TaskDetailsCloudModel, TaskCloudService } from '../../task/public-api'; import { TaskDetailsCloudModel } from '../../start-task/models/task-details-cloud.model';
import { TaskCloudService } from '../../services/task-cloud.service';
import { IdentityUserService, FormOutcomeModel } from '@alfresco/adf-core'; import { IdentityUserService, FormOutcomeModel } from '@alfresco/adf-core';
@Component({ @Component({
@@ -91,7 +92,6 @@ export class TaskFormCloudComponent implements OnChanges {
constructor( constructor(
private taskCloudService: TaskCloudService, private taskCloudService: TaskCloudService,
private identityUserService: IdentityUserService) { private identityUserService: IdentityUserService) {
} }
ngOnChanges(changes: SimpleChanges) { ngOnChanges(changes: SimpleChanges) {
@@ -0,0 +1,20 @@
/*!
* @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.
*/
export * from './components/task-form-cloud.component';
export * from './task-form.module';
@@ -16,24 +16,27 @@
*/ */
import { NgModule } from '@angular/core'; import { NgModule } from '@angular/core';
import { CompleteTaskDirective } from './directives/complete-task.directive'; import { CommonModule } from '@angular/common';
import { TaskCloudService } from './services/task-cloud.service'; import { MaterialModule } from '../../material.module';
import { ClaimTaskDirective } from './directives/claim-task.directive'; import { FormCloudModule } from '../../form/form-cloud.module';
import { UnClaimTaskDirective } from './directives/unclaim-task.directive'; import { TaskDirectiveModule } from '../directives/task-directive.module';
import { TaskFormCloudComponent } from './components/task-form-cloud.component';
import { CoreModule } from '@alfresco/adf-core';
@NgModule({ @NgModule({
imports: [
CoreModule,
CommonModule,
MaterialModule,
FormCloudModule,
TaskDirectiveModule
],
declarations: [ declarations: [
CompleteTaskDirective, TaskFormCloudComponent
ClaimTaskDirective,
UnClaimTaskDirective
], ],
exports: [ exports: [
CompleteTaskDirective, TaskFormCloudComponent
ClaimTaskDirective,
UnClaimTaskDirective
],
providers: [
TaskCloudService
] ]
}) })
export class TaskModule { } export class TaskFormModule { }
+1 -1
View File
@@ -21,5 +21,5 @@ export * from './lib/app/public-api';
export * from './lib/process/public-api'; export * from './lib/process/public-api';
export * from './lib/task/public-api'; export * from './lib/task/public-api';
export * from './lib/group/public-api'; export * from './lib/group/public-api';
export * from './lib/services/public-api';
export * from './lib/form/public-api'; export * from './lib/form/public-api';
export * from './lib/services/public-api';