[ADF-3797] Task management view - Task with Form (#4534)

* [ADF-4248] Created form cloud service

* [ADF-4248] Created form cloud model

* [ADF-4248] Created new cloud form

* [ADF-4248] Exported cloud from module

* [ADF-4248] Added form saving feature

* [ADF-4248] Added form to task details

* [ADF-4248] Added services to save form

* [ADF-4248] Added data support

* [ADF-4248] Added outcome support in form model

* [ADF-4248] Modified demo component to show form

* [ADF-4248] Copied tests

* [ADF-4248] Added form parsing service

* [ADF-4248] Added form cloud demo

* [ADF-4248] Added form input to fom-cloud

* [ADF-4248] Added tests for form cloud model

* [ADF-4248] Improved form model json parsing

* [ADF-4248]  Added test for form could

* [ADF-4248] Refactored types in the form model

* [ADF-4248] Improved tests

* [ADF-4248] Added tests for form cloud service

* [ADF-4248] Added tests for form services

* [ADF-4248] Refactored form services

* [ADF-4248] Handled form events in demo shell

* [ADF-4248] Improved form value parsing

* [ADF-4248] Added form-cloud demo to routing

* [ADF-4248] Added field validation without handler

* [ADF-4248] Added task variable model

* [ADF-4248] Added adf-cloud prefix to css classes

* [ADF-4248] Translated name of nameless task

* [ADF-4248] Added docs for cloud form component

* [ADF-4248] Added docs for cloud form service

* create base component

* [ADF-4248] Created formBase and formModelbase

* [ADF-4248] Used base classes in cloud package

* Update form-cloud.component.md

* Update form-cloud.service.md

* [ADF-4248] Created form cloud service

* [ADF-4248] Created form cloud model

* [ADF-4248] Created new cloud form

* [ADF-4248] Exported cloud from module

* [ADF-4248] Added form saving feature

* [ADF-4248] Added form to task details

* [ADF-4248] Added services to save form

* [ADF-4248] Added data support

* [ADF-4248] Added outcome support in form model

* [ADF-4248] Modified demo component to show form

* [ADF-4248] Copied tests

* [ADF-4248] Added form parsing service

* [ADF-4248] Added form cloud demo

* [ADF-4248] Added form input to fom-cloud

* [ADF-4248] Added tests for form cloud model

* [ADF-4248] Improved form model json parsing

* [ADF-4248]  Added test for form could

* [ADF-4248] Refactored types in the form model

* [ADF-4248] Improved tests

* [ADF-4248] Added tests for form cloud service

* [ADF-4248] Added tests for form services

* [ADF-4248] Refactored form services

* [ADF-4248] Handled form events in demo shell

* [ADF-4248] Improved form value parsing

* [ADF-4248] Added form-cloud demo to routing

* [ADF-4248] Added field validation without handler

* [ADF-4248] Added task variable model

* [ADF-4248] Added adf-cloud prefix to css classes

* [ADF-4248] Translated name of nameless task

* [ADF-4248] Added docs for cloud form component

* [ADF-4248] Added docs for cloud form service

* create base component

* [ADF-4248] Created formBase and formModelbase

* [ADF-4248] Used base classes in cloud package

* [ADF-4248] Moved documentation to process services

* [ADF-4248] Removed duplicate import

* [ADF-4248] Fixed wrong imports

* [ADF-4248] Renamed form renderer input

* [ADF-4248] Show translated name for nameless form

* Enable the uploadWidget

* Make the form great again!

* Move the class style on the parent

* Fix the debugMode
This commit is contained in:
Deepak Paul
2019-04-10 21:40:56 +05:30
committed by Eugenio Romano
parent 61ee1f1d53
commit 558ee4c031
76 changed files with 5029 additions and 450 deletions

View File

@@ -1,6 +1,6 @@
module.exports = {
"/alfresco": {
"target": "http://localhost:8080",
"target": "http://aps2staging.envalfresco.com",
"secure": false,
"pathRewrite": {
"^/alfresco/alfresco": ""

View File

@@ -64,7 +64,7 @@ import { ContentModule } from '@alfresco/adf-content-services';
import { InsightsModule } from '@alfresco/adf-insights';
import { ProcessModule } from '@alfresco/adf-process-services';
import { AuthBearerInterceptor } from './services';
import { ProcessServicesCloudModule, GroupCloudModule, TaskDirectiveModule } from '@alfresco/adf-process-services-cloud';
import { ProcessServicesCloudModule } from '@alfresco/adf-process-services-cloud';
import { AppExtensionsModule } from './app-extension.module';
import { TreeViewSampleComponent } from './components/tree-view/tree-view-sample.component';
import { CloudLayoutComponent } from './components/cloud/cloud-layout.component';
@@ -82,6 +82,7 @@ import { PeopleGroupCloudDemoComponent } from './components/cloud/people-groups-
import { CloudSettingsComponent } from './components/cloud/cloud-settings.component';
import { NestedMenuPositionDirective } from './components/cloud/directives/nested-menu-position.directive';
import { ConfirmDialogExampleComponent } from './components/confirm-dialog/confirm-dialog-example.component';
import { FormCloudDemoComponent } from './components/app-layout/cloud/form-demo/cloud-form-demo.component';
@NgModule({
imports: [
@@ -102,10 +103,7 @@ import { ConfirmDialogExampleComponent } from './components/confirm-dialog/confi
ExtensionsModule.forRoot(),
ThemePickerModule,
ChartsModule,
MonacoEditorModule.forRoot(),
ProcessServicesCloudModule,
GroupCloudModule,
TaskDirectiveModule
MonacoEditorModule.forRoot()
],
declarations: [
AppComponent,
@@ -150,6 +148,8 @@ import { ConfirmDialogExampleComponent } from './components/confirm-dialog/confi
PeopleGroupCloudDemoComponent,
CloudSettingsComponent,
NestedMenuPositionDirective,
ConfirmDialogExampleComponent,
FormCloudDemoComponent,
ConfirmDialogExampleComponent
],
providers: [

View File

@@ -49,6 +49,7 @@ import { StartProcessCloudDemoComponent } from './components/cloud/start-process
import { TaskDetailsCloudDemoComponent } from './components/cloud/task-details-cloud-demo.component';
import { ProcessDetailsCloudDemoComponent } from './components/cloud/process-details-cloud-demo.component';
import { TemplateDemoComponent } from './components/template-list/template-demo.component';
import { FormCloudDemoComponent } from './components/app-layout/cloud/form-demo/cloud-form-demo.component';
import { ConfirmDialogExampleComponent } from './components/confirm-dialog/confirm-dialog-example.component';
export const appRoutes: Routes = [
@@ -355,6 +356,7 @@ export const appRoutes: Routes = [
path: 'icons',
loadChildren: './components/icons/icons.module#AppIconsModule'
},
{ path: 'form-cloud', component: FormCloudDemoComponent },
{ path: 'form', component: FormComponent },
{ path: 'form-list', component: FormListComponent },
{ path: 'form-loading', component: FormLoadingComponent },

View File

@@ -48,6 +48,7 @@ export class AppLayoutComponent implements OnInit {
{ href: '/task-list', icon: 'assignment', title: 'APP_LAYOUT.TASK_LIST' },
{ href: '/cloud', icon: 'cloud', title: 'APP_LAYOUT.PROCESS_CLOUD', children: [
{ href: '/cloud/', icon: 'cloud', title: 'APP_LAYOUT.HOME' },
{ href: '/form-cloud', icon: 'poll', title: 'APP_LAYOUT.FORM' },
{ href: '/cloud/people-group-cloud', icon: 'group', title: 'APP_LAYOUT.PEOPLE_GROUPS_CLOUD' }
]},
{ href: '/activiti', icon: 'device_hub', title: 'APP_LAYOUT.PROCESS_SERVICES', children: [

View File

@@ -0,0 +1,56 @@
<div class="main-content">
<mat-tab-group>
<mat-tab label="Form">
<div class="adf-form-container">
<adf-cloud-form
[showRefreshButton]="false"
[form]="form"
(formSaved)="onFormSaved()"
(formError)="logErrors($event)">
</adf-cloud-form>
</div>
<div class="adf-console" #console>
<h3>Error log:</h3>
<p *ngFor="let error of errorFields">Error {{ error.name }} {{error.validationSummary.message |
translate}}</p>
</div>
</mat-tab>
<mat-tab label="Editor">
<ngx-monaco-editor
id="adf-form-config-editor"
class="adf-form-config-editor"
[options]="editorOptions"
[(ngModel)]="formConfig"
(onInit)="onInitFormEditor($event)">
</ngx-monaco-editor>
<div class="adf-form-editor-buttons">
<button mat-raised-button id="adf-form-config-save" (click)="onSaveFormConfig()" color="primary">Save
form config
</button>
<button mat-raised-button id="adf-form-config-clear" (click)="onClearFormConfig()" color="primary">Clear
form config
</button>
</div>
<div class="adf-upload-config-button">
<a mat-raised-button color="primary" >
<mat-icon>file_upload</mat-icon>
<label for="upload-config-file">Upload JSON File</label>
<input
id="upload-config-file"
data-automation-id="upload-single-file"
type="file"
name="uploadConfig"
accept=".json"
(change)="onConfigAdded($event)">
</a>
</div>
</mat-tab>
</mat-tab-group>
</div>

View File

@@ -0,0 +1,57 @@
.adf-form-container {
padding: 10px;
}
.adf-main-content {
padding: 0 15px;
}
.adf-card-view {
width: 30%;
display: inline-block;
}
.adf-console {
width: 60%;
display: inline-block;
vertical-align: top;
margin-left: 10px;
height: 500px;
overflow: scroll;
padding-bottom: 30px;
h3 {
margin-top: 0;
}
p {
display: block;
font-family: monospace, monospace;
margin: 0;
}
}
.adf-form-config-editor {
height: 500px !important;
}
.adf-form-editor-buttons {
display: flex;
justify-content: space-evenly;
}
.adf-upload-config-button {
display: flex;
justify-content: center;
input {
cursor: pointer;
height: 100%;
right: 0;
opacity: 0;
position: absolute;
top: 0;
width: 300px;
z-index: 4;
}
}

View File

@@ -0,0 +1,109 @@
/*!
* @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, OnDestroy, OnInit } from '@angular/core';
import { FormFieldModel, NotificationService, FormRenderingService } from '@alfresco/adf-core';
import { FormCloud, FormCloudService, UploadCloudWidgetComponent } from '@alfresco/adf-process-services-cloud';
import { Subscription } from 'rxjs';
import { formDefinition } from './demo-form';
@Component({
templateUrl: 'cloud-form-demo.component.html',
styleUrls: ['cloud-form-demo.component.scss']
})
export class FormCloudDemoComponent implements OnInit, OnDestroy {
form: FormCloud;
errorFields: FormFieldModel[] = [];
formConfig: string;
editor: any;
private subscriptions: Subscription[] = [];
editorOptions = {
theme: 'vs-dark',
language: 'json',
autoIndent: true,
formatOnPaste: true,
formatOnType: true,
automaticLayout: true
};
constructor(
private notificationService: NotificationService,
private formRenderingService: FormRenderingService,
private formService: FormCloudService) {
this.formRenderingService.setComponentTypeResolver('upload', () => UploadCloudWidgetComponent, true);
}
logErrors(errorFields: FormFieldModel[]) {
this.errorFields = errorFields;
}
ngOnInit() {
this.formConfig = formDefinition;
this.parseForm();
}
onFormSaved() {
this.notificationService.openSnackMessage('Task has been saved successfully');
}
ngOnDestroy() {
this.subscriptions.forEach((subscription) => subscription.unsubscribe());
this.subscriptions = [];
}
onInitFormEditor(editor) {
this.editor = editor;
setTimeout(() => {
this.editor.getAction('editor.action.formatDocument').run();
}, 1000);
}
parseForm() {
this.form = this.formService.parseForm(JSON.parse(this.formConfig));
}
onSaveFormConfig() {
try {
this.parseForm();
} catch (error) {
this.notificationService.openSnackMessage(
'Wrong form configuration',
4000
);
}
}
onClearFormConfig() {
this.formConfig = '';
}
onConfigAdded($event: any): void {
const file = $event.currentTarget.files[0];
const fileReader = new FileReader();
fileReader.onload = () => {
this.formConfig = <string> fileReader.result;
};
fileReader.readAsText(file);
this.onInitFormEditor(this.editor);
$event.target.value = '';
}
}

View File

@@ -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.
*/
export const formDefinition = `{
"formRepresentation": {
"id": "text-form",
"name": "test-start-form",
"version": 0,
"description": "",
"formDefinition": {
"tabs": [],
"fields": [
{
"id": "1511517333638",
"type": "container",
"fieldType": "ContainerRepresentation",
"name": "Label",
"tab": null,
"numberOfColumns": 2,
"fields": {
"1": [
{
"fieldType": "FormFieldRepresentation",
"id": "texttest",
"name": "texttest",
"type": "text",
"value": null,
"required": false,
"placeholder": "text",
"params": {
"existingColspan": 2,
"maxColspan": 6,
"inputMaskReversed": true,
"inputMask": "0#",
"inputMaskPlaceholder": "(0-9)"
}
}
],
"2": [{
"fieldType": "AttachFileFieldRepresentation",
"id": "attachfiletest",
"name": "attachfiletest",
"type": "upload",
"required": true,
"colspan": 2,
"placeholder": "attachfile",
"params": {
"existingColspan": 2,
"maxColspan": 2,
"fileSource": {
"serviceId": "local-file",
"name": "Local File"
},
"multiple": true,
"link": false
},
"visibilityCondition": {
}
}]
}
}
],
"outcomes": [],
"metadata": {
"property1": "value1",
"property2": "value2"
},
"variables": [
{
"name": "variable1",
"type": "string",
"value": "value1"
},
{
"name": "variable2",
"type": "string",
"value": "value2"
}
]
}
}}
`;

View File

@@ -1,19 +1,30 @@
<h4 data-automation-id="task-details-header">Simple page to show the taskId: {{ taskId }} of the app: {{ appName }}</h4>
<div class="adf-task-detail-container">
<div class="adf-task-control">
<button mat-button (click)="goBack()">Cancel</button>
<button mat-button color="primary" *ngIf="canCompleteTask()" adf-cloud-complete-task [appName]="appName" [taskId]="taskId"
(success)="onCompletedTask()">{{ 'ADF_TASK_LIST.DETAILS.BUTTON.COMPLETE' | translate }}</button>
<button mat-button color="primary" *ngIf="canClaimTask()" adf-cloud-claim-task [appName]="appName" [taskId]="taskId"
(success)="onClaimTask()">{{ 'ADF_CLOUD_TASK_HEADER.BUTTON.CLAIM' | translate }}</button>
<button mat-button color="primary" *ngIf="canUnClaimTask()" adf-cloud-unclaim-task [appName]="appName" [taskId]="taskId"
(success)="onUnclaimTask()">{{ 'ADF_CLOUD_TASK_HEADER.BUTTON.RELEASE' | translate }}</button>
<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%"
[appName]="appName"
[taskId]="taskId"
(formCompleted)="onTaskCompleted()"
(formSaved)="onFormSaved()">
</adf-cloud-form>
</div>
<adf-cloud-task-header fxFlex
[appName]="appName"
[taskId]="taskId"
[readOnly]="readOnly">
</adf-cloud-task-header>
</div>
<adf-cloud-task-header class="adf-demop-card-container" [appName]="appName" [taskId]="taskId" [readOnly]="readOnly">
</adf-cloud-task-header>
</div>
</div>

View File

@@ -17,7 +17,8 @@
import { Component, OnInit } from '@angular/core';
import { ActivatedRoute, Router } from '@angular/router';
import { TaskDetailsCloudModel, TaskCloudService } from '@alfresco/adf-process-services-cloud';
import { TaskDetailsCloudModel, TaskCloudService, UploadCloudWidgetComponent } from '@alfresco/adf-process-services-cloud';
import { NotificationService, FormRenderingService } from '@alfresco/adf-core';
@Component({
templateUrl: './task-details-cloud-demo.component.html',
@@ -33,7 +34,9 @@ export class TaskDetailsCloudDemoComponent implements OnInit {
constructor(
private route: ActivatedRoute,
private router: Router,
private taskCloudService: TaskCloudService
private formRenderingService: FormRenderingService,
private taskCloudService: TaskCloudService,
private notificationService: NotificationService
) {
this.route.params.subscribe((params) => {
this.taskId = params.taskId;
@@ -41,6 +44,8 @@ export class TaskDetailsCloudDemoComponent implements OnInit {
this.route.parent.params.subscribe((params) => {
this.appName = params.appName;
});
this.formRenderingService.setComponentTypeResolver('upload', () => UploadCloudWidgetComponent, true);
}
ngOnInit() {
@@ -59,7 +64,7 @@ export class TaskDetailsCloudDemoComponent implements OnInit {
}
canCompleteTask(): boolean {
return this.taskDetails && this.taskCloudService.canCompleteTask(this.taskDetails);
return this.taskDetails && !this.taskDetails.formKey && this.taskCloudService.canCompleteTask(this.taskDetails);
}
canClaimTask(): boolean {
@@ -70,6 +75,10 @@ export class TaskDetailsCloudDemoComponent implements OnInit {
return this.taskDetails && this.taskCloudService.canUnclaimTask(this.taskDetails);
}
hasTaskForm(): boolean {
return this.taskDetails && this.taskDetails.formKey;
}
goBack() {
this.router.navigate([`/cloud/${this.appName}/`]);
}
@@ -85,4 +94,12 @@ export class TaskDetailsCloudDemoComponent implements OnInit {
onClaimTask() {
this.goBack();
}
onTaskCompleted() {
this.goBack();
}
onFormSaved() {
this.notificationService.openSnackMessage('Task has been saved successfully');
}
}

View File

@@ -16,7 +16,8 @@
*/
import { Component, ViewChild } from '@angular/core';
import { FormComponent, FormModel, FormService, LogService, FormOutcomeEvent } from '@alfresco/adf-core';
import { FormModel, FormService, LogService, FormOutcomeEvent } from '@alfresco/adf-core';
import { FormComponent } from '@alfresco/adf-process-services';
@Component({
selector: 'app-form-list',

View File

@@ -74,7 +74,7 @@ export class InMemoryFormService extends FormService {
if (!json.fields) {
form.outcomes = [
new FormOutcomeModel(form, {
id: '$custom',
id: '$save',
name: FormOutcomeModel.SAVE_ACTION,
isSystem: true
})