[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 - 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 { AppListCloudPage } from '@alfresco/adf-testing';
import { StringUtil } from '@alfresco/adf-testing';
import { browser } from 'protractor';
import resources = require('../util/resources');
describe('Task list cloud - selection', () => {
@ -58,6 +57,10 @@ describe('Task list cloud - selection', () => {
tasks.push(response.entry.name);
}
done();
});
beforeEach(async (done) => {
navigationBarPage.navigateToProcessServicesCloudPage();
appListCloudComponent.checkApsContainer();
appListCloudComponent.goToApp(simpleApp);
@ -67,12 +70,6 @@ describe('Task list cloud - selection', () => {
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', () => {
tasksCloudDemoPage.clickSettingsButton().selectSelectionMode('None');
tasksCloudDemoPage.clickSettingsButton().disableDisplayTaskDetails();
@ -107,6 +104,7 @@ describe('Task list cloud - selection', () => {
tasksCloudDemoPage.clickAppButton();
tasksCloudDemoPage.taskListCloudComponent().getDataTable().waitForTableBody();
tasksCloudDemoPage.taskListCloudComponent().checkContentIsDisplayedByName(tasks[0]);
tasksCloudDemoPage.taskListCloudComponent().selectRow(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 { TemplateModule, FormBaseModule, PipeModule, CoreModule } from '@alfresco/adf-core';
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 { TaskCloudModule } from '../task/task-cloud.module';
import { FormCloudComponent } from './components/form-cloud.component';
import { FormDefinitionSelectorCloudComponent } from './components/form-definition-selector-cloud.component';
import { FormDefinitionSelectorCloudService } from './services/form-definition-selector-cloud.service';
@NgModule({
imports: [
CommonModule,
PipeModule,
CommonModule,
PipeModule,
TemplateModule,
FlexLayoutModule,
MaterialModule,
FormsModule,
ReactiveFormsModule,
FormBaseModule,
CoreModule,
TaskCloudModule
CoreModule
],
declarations: [FormCloudComponent, UploadCloudWidgetComponent, TaskFormCloudComponent],
declarations: [FormCloudComponent, UploadCloudWidgetComponent, FormDefinitionSelectorCloudComponent],
providers: [FormDefinitionSelectorCloudService],
entryComponents: [
UploadCloudWidgetComponent
],
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/task-variable-cloud.model';
export * from './models/form-definition-selector-cloud.model';
export * from './components/form-cloud.component';
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-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",
"CANDIDATE_GROUP": "Candidate Group",
"FORM": "Form",
"DATE": "Choose Date"
"DATE": "Choose Date",
"NONE": "None"
},
"ACTION": {
"START": "Start",

@ -22,6 +22,7 @@ import { TaskCloudModule } from './task/task-cloud.module';
import { ProcessCloudModule } from './process/process-cloud.module';
import { GroupCloudModule } from './group/group-cloud.module';
import { FormCloudModule } from './form/form-cloud.module';
import { TaskFormModule } from './task/task-form/task-form.module';
@NgModule({
imports: [
@ -30,7 +31,8 @@ import { FormCloudModule } from './form/form-cloud.module';
ProcessCloudModule,
TaskCloudModule,
GroupCloudModule,
FormCloudModule
FormCloudModule,
TaskFormModule
],
providers: [
{
@ -47,7 +49,8 @@ import { FormCloudModule } from './form/form-cloud.module';
ProcessCloudModule,
TaskCloudModule,
GroupCloudModule,
FormCloudModule
FormCloudModule,
TaskFormModule
]
})
export class ProcessServicesCloudModule { }

@ -1,13 +1,13 @@
@import './../app/components/app-details-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 './../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) {

@ -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 './start-task/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 './directives/task-directive.module';
export * from './services/task-cloud.service';

@ -1,10 +1,11 @@
<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-header>
<form [formGroup]="taskForm" fxLayout="column" (ngSubmit)="saveTask()">
<mat-card-content>
<mat-card-content>
<div class="adf-task-name">
<mat-form-field fxFlex>
<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 }}
</mat-error>
<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-form-field>
</div>
@ -34,25 +35,25 @@
<mat-form-field fxFlex>
<div style="height: 40px;">
<input matInput type="number" placeholder="Priority" formControlName="priority">
<input matInput type="number" placeholder="Priority" formControlName="priority">
</div>
</mat-form-field>
</div>
<div fxLayout="row" fxLayout.lt-md="column" fxLayoutGap="20px" fxLayoutGap.lt-md="0px">
<mat-form-field fxFlex>
<input matInput
[matDatepicker]="taskDatePicker"
(keydown)="true"
(focusout)="onDateChanged($event.srcElement.value)"
placeholder="{{'ADF_CLOUD_TASK_LIST.START_TASK.FORM.LABEL.DATE'|translate}}"
[(ngModel)]="dueDate"
[ngModelOptions]="{standalone: true}"
id="date_id">
<mat-datepicker-toggle matSuffix [for]="taskDatePicker"></mat-datepicker-toggle>
<mat-datepicker #taskDatePicker
[touchUi]="true"
(dateChanged)="onDateChanged($event)">
</mat-datepicker>
[matDatepicker]="taskDatePicker"
(keydown)="true"
(focusout)="onDateChanged($event.srcElement.value)"
placeholder="{{'ADF_CLOUD_TASK_LIST.START_TASK.FORM.LABEL.DATE'|translate}}"
[(ngModel)]="dueDate"
[ngModelOptions]="{standalone: true}"
id="date_id">
<mat-datepicker-toggle matSuffix [for]="taskDatePicker"></mat-datepicker-toggle>
<mat-datepicker #taskDatePicker
[touchUi]="true"
(dateChanged)="onDateChanged($event)">
</mat-datepicker>
<div class="adf-cloud-date-error-container">
<div *ngIf="dateError">
<div class="adf-error-text">{{'ADF_CLOUD_START_TASK.ERROR.DATE' | translate}}</div>
@ -61,41 +62,46 @@
</div>
</mat-form-field>
<adf-cloud-people fxFlex #peopleInput *ngIf="currentUser"
[appName]="appName"
[preSelectUsers]="[currentUser]"
(selectUser)="onAssigneeSelect($event)"
[title]="'ADF_TASK_LIST.START_TASK.FORM.LABEL.ASSIGNEE'"
(removeUser)="onAssigneeRemove()"></adf-cloud-people>
[appName]="appName"
[preSelectUsers]="[currentUser]"
(selectUser)="onAssigneeSelect($event)"
[title]="'ADF_TASK_LIST.START_TASK.FORM.LABEL.ASSIGNEE'"
(removeUser)="onAssigneeRemove()"></adf-cloud-people>
</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"
[mode]="'multiple'"
[title]="'ADF_CLOUD_TASK_LIST.START_TASK.FORM.LABEL.CANDIDATE_GROUP'"
[appName]="appName"
(selectGroup)="onCandiateGroupSelect($event)"
(removeGroup)="onCandiateGroupRemove($event)"></adf-cloud-group>
<div fxFlex></div>
[mode]="'multiple'"
[title]="'ADF_CLOUD_TASK_LIST.START_TASK.FORM.LABEL.CANDIDATE_GROUP'"
[appName]="appName"
(selectGroup)="onCandidateGroupSelect($event)"
(removeGroup)="onCandidateGroupRemove($event)">
</adf-cloud-group>
<adf-cloud-form-definition-selector fxFlex
[appName]="appName"
(selectForm)="onFormSelect($event)">
</adf-cloud-form-definition-selector>
</div>
</mat-card-content>
</mat-card-content>
<mat-card-actions>
<div class="adf-cloud-start-task-footer" fxLayout="row" fxLayoutAlign="end end" >
<button
mat-button
type="button"
(click)="onCancel()"
id="button-cancel">
{{'ADF_CLOUD_TASK_LIST.START_TASK.FORM.ACTION.CANCEL'|translate}}
</button>
<button
color="primary"
type="submit" [disabled]="dateError || !taskForm.valid || submitted || assignee.hasError() || candidateGroups.hasError()"
mat-button
id="button-start">
{{'ADF_CLOUD_TASK_LIST.START_TASK.FORM.ACTION.START'|translate}}
</button>
</div>
</mat-card-actions>
<mat-card-actions>
<div class="adf-cloud-start-task-footer" fxLayout="row" fxLayoutAlign="end end">
<button
mat-button
type="button"
(click)="onCancel()"
id="button-cancel">
{{'ADF_CLOUD_TASK_LIST.START_TASK.FORM.ACTION.CANCEL'|translate}}
</button>
<button
color="primary"
type="submit"
[disabled]="dateError || !taskForm.valid || submitted || assignee.hasError() || candidateGroups.hasError()"
mat-button
id="button-start">
{{'ADF_CLOUD_TASK_LIST.START_TASK.FORM.ACTION.START'|translate}}
</button>
</div>
</mat-card-actions>
</form>
</mat-card>

@ -33,6 +33,7 @@ import { CUSTOM_ELEMENTS_SCHEMA } from '@angular/core';
import { ProcessServiceCloudTestingModule } from './../../../testing/process-service-cloud.testing.module';
import { StartTaskCloudTestingModule } from '../testing/start-task-cloud.testing.module';
import { TaskDetailsCloudModel } from '../models/task-details-cloud.model';
import { FormDefinitionSelectorCloudService } from '../../../form/services/form-definition-selector-cloud.service';
describe('StartTaskCloudComponent', () => {
@ -40,12 +41,21 @@ describe('StartTaskCloudComponent', () => {
let fixture: ComponentFixture<StartTaskCloudComponent>;
let service: StartTaskCloudService;
let identityService: IdentityUserService;
let formDefinitionSelectorCloudService: FormDefinitionSelectorCloudService;
let element: HTMLElement;
let createNewTaskSpy: jasmine.Spy;
setupTestBed({
imports: [ProcessServiceCloudTestingModule, StartTaskCloudTestingModule],
providers: [StartTaskCloudService, AlfrescoApiService, AppConfigService, LogService, StorageService, UserPreferencesService],
providers: [
StartTaskCloudService,
AlfrescoApiService,
AppConfigService,
LogService,
StorageService,
UserPreferencesService,
FormDefinitionSelectorCloudService
],
schemas: [ CUSTOM_ELEMENTS_SCHEMA ]
});
@ -56,8 +66,10 @@ describe('StartTaskCloudComponent', () => {
service = TestBed.get(StartTaskCloudService);
identityService = TestBed.get(IdentityUserService);
formDefinitionSelectorCloudService = TestBed.get(FormDefinitionSelectorCloudService);
createNewTaskSpy = spyOn(service, 'createNewTask').and.returnValue(of(taskDetailsMock));
spyOn(identityService, 'getCurrentUserInfo').and.returnValue(new IdentityUserModel({username: 'currentUser', firstName: 'Test', lastName: 'User'}));
spyOn(formDefinitionSelectorCloudService, 'getForms').and.returnValue(of([]));
fixture.detectChanges();
}));

@ -97,6 +97,8 @@ export class StartTaskCloudComponent implements OnInit, OnDestroy {
currentUser: IdentityUserModel;
formKey: string;
private localeSub: Subscription;
private createTaskSub: Subscription;
@ -112,7 +114,6 @@ export class StartTaskCloudComponent implements OnInit, OnDestroy {
this.userPreferencesService.select(UserPreferenceValues.Locale).subscribe((locale) => {
this.dateAdapter.setLocale(locale);
});
this.loadCurrentUser();
this.buildForm();
}
@ -131,7 +132,8 @@ export class StartTaskCloudComponent implements OnInit, OnDestroy {
this.taskForm = this.formBuilder.group({
name: new FormControl(this.name, [Validators.required, Validators.maxLength(this.getMaxNameLength()), this.whitespaceValidator]),
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.dueDate = this.dueDate;
newTask.assignee = this.assigneeName;
newTask.formKey = this.formKey;
newTask.candidateGroups = this.candidateGroupNames;
this.createNewTask(new TaskDetailsCloudModel(newTask));
}
@ -192,15 +196,17 @@ export class StartTaskCloudComponent implements OnInit, OnDestroy {
this.assigneeName = '';
}
onCandiateGroupSelect(candidateGroup: any) {
onCandidateGroupSelect(candidateGroup: any) {
if (candidateGroup.name) {
this.candidateGroupNames.push(candidateGroup.name);
}
}
onCandiateGroupRemove(candidateGroup: any) {
onCandidateGroupRemove(candidateGroup: any) {
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 {
return this.taskForm.get('priority');
}
onFormSelect(formKey: string) {
this.formKey = formKey || '';
}
}

@ -24,6 +24,7 @@ export class StartTaskCloudRequestModel {
candidateUsers: string[];
candidateGroups: string[];
payloadType: string;
formKey: string;
constructor(obj?: any) {
if (obj) {
@ -34,6 +35,7 @@ export class StartTaskCloudRequestModel {
this.dueDate = obj.dueDate || null;
this.candidateUsers = obj.candidateUsers || null;
this.candidateGroups = obj.candidateGroups || null;
this.formKey = obj.formKey || null;
this.payloadType = 'CreateTaskPayload';
}
}

@ -25,27 +25,31 @@ import { StartTaskCloudService } from './services/start-task-cloud.service';
import { FormsModule, ReactiveFormsModule } from '@angular/forms';
import { PeopleCloudComponent } from './components/people-cloud/people-cloud.component';
import { GroupCloudModule } from '../../group/group-cloud.module';
import { TaskCloudService } from '../services/task-cloud.service';
import { FormCloudModule } from '../../form/form-cloud.module';
@NgModule({
imports: [
CommonModule,
PipeModule,
CommonModule,
PipeModule,
TemplateModule,
FlexLayoutModule,
MaterialModule,
FormsModule,
ReactiveFormsModule,
GroupCloudModule,
GroupCloudModule,
CoreModule
CoreModule,
FormCloudModule
],
declarations: [StartTaskCloudComponent, PeopleCloudComponent],
providers: [
StartTaskCloudService
],
StartTaskCloudService,
TaskCloudService
],
exports: [
StartTaskCloudComponent,
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 { TaskHeaderCloudModule } from './task-header/task-header-cloud.module';
import { TaskDirectiveModule } from './directives/task-directive.module';
import { TaskFormModule } from './task-form/task-form.module';
@NgModule({
imports: [
@ -28,14 +29,16 @@ import { TaskDirectiveModule } from './directives/task-directive.module';
TaskFiltersCloudModule,
StartTaskCloudModule,
TaskHeaderCloudModule,
TaskDirectiveModule
TaskDirectiveModule,
TaskFormModule
],
exports: [
TaskListCloudModule,
TaskFiltersCloudModule,
StartTaskCloudModule,
TaskHeaderCloudModule,
TaskDirectiveModule
TaskDirectiveModule,
TaskFormModule
]
})
export class TaskCloudModule { }

@ -17,6 +17,9 @@
export * from './components/task-filters-cloud.component';
export * from './components/edit-task-filter-cloud.component';
export * from './models/filter-cloud.model';
export * from './services/task-filter-cloud.service';
export * from './task-filters-cloud.module';

@ -15,12 +15,14 @@
* limitations under the License.
*/
import { ProcessServiceCloudTestingModule } from '../../testing/process-service-cloud.testing.module';
import { FormCloudModule } from '../form-cloud.module';
import { ProcessServiceCloudTestingModule } from '../../../testing/process-service-cloud.testing.module';
import { TaskCloudModule } from '../../task-cloud.module';
import { TaskDirectiveModule } from '../../directives/task-directive.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 { TaskDetailsCloudModel } from '../../start-task/models/task-details-cloud.model';
import { TaskCloudService } from '../../services/task-cloud.service';
import { of } from 'rxjs';
import { DebugElement, CUSTOM_ELEMENTS_SCHEMA, SimpleChange } from '@angular/core';
import { By } from '@angular/platform-browser';
@ -52,14 +54,14 @@ describe('TaskFormCloudComponent', () => {
let fixture: ComponentFixture<TaskFormCloudComponent>;
setupTestBed({
imports: [ProcessServiceCloudTestingModule, FormCloudModule],
imports: [ProcessServiceCloudTestingModule, TaskCloudModule, TaskDirectiveModule],
schemas: [CUSTOM_ELEMENTS_SCHEMA]
});
beforeEach(() => {
taskDetails.status = 'ASSIGNED';
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);
getTaskSpy = spyOn(taskCloudService, 'getTaskById').and.returnValue(of(new TaskDetailsCloudModel(taskDetails)));
@ -74,6 +76,7 @@ describe('TaskFormCloudComponent', () => {
});
describe('Complete button', () => {
it('should show complete button when status is ASSIGNED', async(() => {
component.appName = 'app1';
component.taskId = 'task1';
@ -115,6 +118,7 @@ describe('TaskFormCloudComponent', () => {
});
describe('Claim/Unclaim buttons', () => {
it('should show unclaim button when status is ASSIGNED', async(() => {
component.appName = 'app1';
component.taskId = 'task1';
@ -185,6 +189,7 @@ describe('TaskFormCloudComponent', () => {
});
describe('Cancel button', () => {
it('should show cancel button by default', async(() => {
component.appName = 'app1';
component.taskId = 'task1';
@ -212,6 +217,7 @@ describe('TaskFormCloudComponent', () => {
});
describe('Inputs', () => {
it('should not show complete/claim/unclaim buttons when readOnly=true', async(() => {
component.appName = 'app1';
component.taskId = 'task1';
@ -258,6 +264,7 @@ describe('TaskFormCloudComponent', () => {
});
describe('Events', () => {
it('should emit cancelClick when cancel button is clicked', (done) => {
component.appName = 'app1';
component.taskId = 'task1';

@ -19,8 +19,9 @@ 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 { FormCloud } from '../../../form/models/form-cloud.model';
import { TaskDetailsCloudModel } from '../../start-task/models/task-details-cloud.model';
import { TaskCloudService } from '../../services/task-cloud.service';
import { IdentityUserService, FormOutcomeModel } from '@alfresco/adf-core';
@Component({
@ -91,7 +92,6 @@ export class TaskFormCloudComponent implements OnChanges {
constructor(
private taskCloudService: TaskCloudService,
private identityUserService: IdentityUserService) {
}
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 { CompleteTaskDirective } from './directives/complete-task.directive';
import { TaskCloudService } from './services/task-cloud.service';
import { ClaimTaskDirective } from './directives/claim-task.directive';
import { UnClaimTaskDirective } from './directives/unclaim-task.directive';
import { CommonModule } from '@angular/common';
import { MaterialModule } from '../../material.module';
import { FormCloudModule } from '../../form/form-cloud.module';
import { TaskDirectiveModule } from '../directives/task-directive.module';
import { TaskFormCloudComponent } from './components/task-form-cloud.component';
import { CoreModule } from '@alfresco/adf-core';
@NgModule({
imports: [
CoreModule,
CommonModule,
MaterialModule,
FormCloudModule,
TaskDirectiveModule
],
declarations: [
CompleteTaskDirective,
ClaimTaskDirective,
UnClaimTaskDirective
TaskFormCloudComponent
],
exports: [
CompleteTaskDirective,
ClaimTaskDirective,
UnClaimTaskDirective
],
providers: [
TaskCloudService
TaskFormCloudComponent
]
})
export class TaskModule { }
export class TaskFormModule { }

@ -21,5 +21,5 @@ export * from './lib/app/public-api';
export * from './lib/process/public-api';
export * from './lib/task/public-api';
export * from './lib/group/public-api';
export * from './lib/services/public-api';
export * from './lib/form/public-api';
export * from './lib/services/public-api';