From 41bbf0b3d1cfe47e964c8bbc82982309f3b85828 Mon Sep 17 00:00:00 2001 From: "tomek.hanaj" Date: Thu, 5 Dec 2024 15:55:24 +0100 Subject: [PATCH 1/8] poc --- lib/core/src/lib/form/public-api.ts | 1 + .../services/screen-rendering.service.spec.ts | 33 +++ .../form/services/screen-rendering.service.ts | 24 ++ .../screen-cloud/screen-cloud.component.html | 3 + .../screen-cloud/screen-cloud.component.scss | 3 + .../screen-cloud.component.spec.ts | 38 +++ .../screen-cloud/screen-cloud.component.ts | 51 ++++ .../components/task-form-cloud.component.html | 73 ----- .../task-form-cloud.component.html | 42 +++ .../task-form-cloud.component.scss | 19 -- .../task-form-cloud.component.spec.ts | 14 +- .../task-form-cloud.component.stories.ts | 16 +- .../task-form-cloud.component.ts | 91 ++----- .../user-task-cloud-buttons.component.html | 27 ++ .../user-task-cloud-buttons.component.scss | 3 + .../user-task-cloud-buttons.component.spec.ts | 37 +++ .../user-task-cloud-buttons.component.ts | 71 +++++ .../user-task-cloud.component.html | 88 ++++++ .../user-task-cloud.component.scss | 10 + .../user-task-cloud.component.spec.ts | 37 +++ .../user-task-cloud.component.ts | 250 ++++++++++++++++++ .../user-task-cloud.interface.ts | 26 ++ .../src/lib/task/task-form/public-api.ts | 3 +- .../lib/task/task-form/task-form.module.ts | 23 +- 24 files changed, 787 insertions(+), 196 deletions(-) create mode 100644 lib/core/src/lib/form/services/screen-rendering.service.spec.ts create mode 100644 lib/core/src/lib/form/services/screen-rendering.service.ts create mode 100644 lib/process-services-cloud/src/lib/screen/components/screen-cloud/screen-cloud.component.html create mode 100644 lib/process-services-cloud/src/lib/screen/components/screen-cloud/screen-cloud.component.scss create mode 100644 lib/process-services-cloud/src/lib/screen/components/screen-cloud/screen-cloud.component.spec.ts create mode 100644 lib/process-services-cloud/src/lib/screen/components/screen-cloud/screen-cloud.component.ts delete mode 100644 lib/process-services-cloud/src/lib/task/task-form/components/task-form-cloud.component.html create mode 100644 lib/process-services-cloud/src/lib/task/task-form/components/task-form-cloud/task-form-cloud.component.html rename lib/process-services-cloud/src/lib/task/task-form/components/{ => task-form-cloud}/task-form-cloud.component.scss (67%) rename lib/process-services-cloud/src/lib/task/task-form/components/{ => task-form-cloud}/task-form-cloud.component.spec.ts (97%) rename lib/process-services-cloud/src/lib/task/task-form/components/{ => task-form-cloud}/task-form-cloud.component.stories.ts (91%) rename lib/process-services-cloud/src/lib/task/task-form/components/{ => task-form-cloud}/task-form-cloud.component.ts (73%) create mode 100644 lib/process-services-cloud/src/lib/task/task-form/components/user-task-cloud-buttons/user-task-cloud-buttons.component.html create mode 100644 lib/process-services-cloud/src/lib/task/task-form/components/user-task-cloud-buttons/user-task-cloud-buttons.component.scss create mode 100644 lib/process-services-cloud/src/lib/task/task-form/components/user-task-cloud-buttons/user-task-cloud-buttons.component.spec.ts create mode 100644 lib/process-services-cloud/src/lib/task/task-form/components/user-task-cloud-buttons/user-task-cloud-buttons.component.ts create mode 100644 lib/process-services-cloud/src/lib/task/task-form/components/user-task-cloud/user-task-cloud.component.html create mode 100644 lib/process-services-cloud/src/lib/task/task-form/components/user-task-cloud/user-task-cloud.component.scss create mode 100644 lib/process-services-cloud/src/lib/task/task-form/components/user-task-cloud/user-task-cloud.component.spec.ts create mode 100644 lib/process-services-cloud/src/lib/task/task-form/components/user-task-cloud/user-task-cloud.component.ts create mode 100644 lib/process-services-cloud/src/lib/task/task-form/components/user-task-cloud/user-task-cloud.interface.ts diff --git a/lib/core/src/lib/form/public-api.ts b/lib/core/src/lib/form/public-api.ts index ba1d920878c..e89e9fb0647 100644 --- a/lib/core/src/lib/form/public-api.ts +++ b/lib/core/src/lib/form/public-api.ts @@ -28,6 +28,7 @@ export * from './services/form-rendering.service'; export * from './services/form.service'; export * from './services/form-validation-service.interface'; export * from './services/widget-visibility.service'; +export * from './services/screen-rendering.service'; export * from './pipes'; diff --git a/lib/core/src/lib/form/services/screen-rendering.service.spec.ts b/lib/core/src/lib/form/services/screen-rendering.service.spec.ts new file mode 100644 index 00000000000..d3a2e89cde8 --- /dev/null +++ b/lib/core/src/lib/form/services/screen-rendering.service.spec.ts @@ -0,0 +1,33 @@ +/*! + * @license + * Copyright © 2005-2024 Hyland Software, Inc. and its affiliates. All rights reserved. + * + * 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 { TestBed } from '@angular/core/testing'; + +import { ScreenRenderingService } from './screen-rendering.service'; + +describe('ScreenRenderingService', () => { + let service: ScreenRenderingService; + + beforeEach(() => { + TestBed.configureTestingModule({}); + service = TestBed.inject(ScreenRenderingService); + }); + + it('should be created', () => { + expect(service).toBeTruthy(); + }); +}); diff --git a/lib/core/src/lib/form/services/screen-rendering.service.ts b/lib/core/src/lib/form/services/screen-rendering.service.ts new file mode 100644 index 00000000000..593ac83c80f --- /dev/null +++ b/lib/core/src/lib/form/services/screen-rendering.service.ts @@ -0,0 +1,24 @@ +/*! + * @license + * Copyright © 2005-2024 Hyland Software, Inc. and its affiliates. All rights reserved. + * + * 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 { DynamicComponentMapper } from '../../common/services/dynamic-component-mapper.service'; + +@Injectable({ + providedIn: 'root' +}) +export class ScreenRenderingService extends DynamicComponentMapper {} diff --git a/lib/process-services-cloud/src/lib/screen/components/screen-cloud/screen-cloud.component.html b/lib/process-services-cloud/src/lib/screen/components/screen-cloud/screen-cloud.component.html new file mode 100644 index 00000000000..3e609e3baaf --- /dev/null +++ b/lib/process-services-cloud/src/lib/screen/components/screen-cloud/screen-cloud.component.html @@ -0,0 +1,3 @@ +
+
+
diff --git a/lib/process-services-cloud/src/lib/screen/components/screen-cloud/screen-cloud.component.scss b/lib/process-services-cloud/src/lib/screen/components/screen-cloud/screen-cloud.component.scss new file mode 100644 index 00000000000..d16f66ad282 --- /dev/null +++ b/lib/process-services-cloud/src/lib/screen/components/screen-cloud/screen-cloud.component.scss @@ -0,0 +1,3 @@ +div { + background-color: inherit; +} diff --git a/lib/process-services-cloud/src/lib/screen/components/screen-cloud/screen-cloud.component.spec.ts b/lib/process-services-cloud/src/lib/screen/components/screen-cloud/screen-cloud.component.spec.ts new file mode 100644 index 00000000000..c9dc04ae55f --- /dev/null +++ b/lib/process-services-cloud/src/lib/screen/components/screen-cloud/screen-cloud.component.spec.ts @@ -0,0 +1,38 @@ +/*! + * @license + * Copyright © 2005-2024 Hyland Software, Inc. and its affiliates. All rights reserved. + * + * 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 { ScreenCloudComponent } from './screen-cloud.component'; + +describe('ScreenCloudComponent', () => { + let component: ScreenCloudComponent; + let fixture: ComponentFixture; + + beforeEach(() => { + TestBed.configureTestingModule({ + imports: [ScreenCloudComponent] + }); + fixture = TestBed.createComponent(ScreenCloudComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/lib/process-services-cloud/src/lib/screen/components/screen-cloud/screen-cloud.component.ts b/lib/process-services-cloud/src/lib/screen/components/screen-cloud/screen-cloud.component.ts new file mode 100644 index 00000000000..e50e0e8274f --- /dev/null +++ b/lib/process-services-cloud/src/lib/screen/components/screen-cloud/screen-cloud.component.ts @@ -0,0 +1,51 @@ +/*! + * @license + * Copyright © 2005-2024 Hyland Software, Inc. and its affiliates. All rights reserved. + * + * 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. + */ + +// FormRenderingService, +import { DynamicComponentModel, ScreenRenderingService } from '@alfresco/adf-core'; +import { CommonModule } from '@angular/common'; +import { Component, ComponentRef, inject, Input, OnInit, ViewChild, ViewContainerRef } from '@angular/core'; + +@Component({ + selector: 'adf-cloud-screen-cloud', + standalone: true, + imports: [CommonModule], + templateUrl: './screen-cloud.component.html', + styleUrls: ['./screen-cloud.component.scss'] +}) +export class ScreenCloudComponent implements OnInit { + /** Task id to fetch corresponding form and values. */ + @Input() taskId: string; + /** App id to fetch corresponding form and values. */ + @Input() + appName: string = ''; + /** Toggle readonly state of the task. */ + @Input() + readOnly = false; + + @ViewChild('container', { read: ViewContainerRef, static: true }) + container: ViewContainerRef; + screenComponent: DynamicComponentModel = { type: 'screen-one' }; + componentRef: ComponentRef; + + private readonly screenRenderingService = inject(ScreenRenderingService); + + ngOnInit() { + const componentType = this.screenRenderingService.resolveComponentType(this.screenComponent); + this.componentRef = this.container.createComponent(componentType); + } +} diff --git a/lib/process-services-cloud/src/lib/task/task-form/components/task-form-cloud.component.html b/lib/process-services-cloud/src/lib/task/task-form/components/task-form-cloud.component.html deleted file mode 100644 index 05a1d244644..00000000000 --- a/lib/process-services-cloud/src/lib/task/task-form/components/task-form-cloud.component.html +++ /dev/null @@ -1,73 +0,0 @@ -
- - - - - - - - - - - -

- - {{ taskDetails?.name || 'FORM.FORM_RENDERER.NAMELESS_TASK' | translate }} - -

-
-
- - - - - - - - -
-
- - - - - -
- - - - diff --git a/lib/process-services-cloud/src/lib/task/task-form/components/task-form-cloud/task-form-cloud.component.html b/lib/process-services-cloud/src/lib/task/task-form/components/task-form-cloud/task-form-cloud.component.html new file mode 100644 index 00000000000..3692ce7d20c --- /dev/null +++ b/lib/process-services-cloud/src/lib/task/task-form/components/task-form-cloud/task-form-cloud.component.html @@ -0,0 +1,42 @@ +
+ + + + + + + + + + + +
diff --git a/lib/process-services-cloud/src/lib/task/task-form/components/task-form-cloud.component.scss b/lib/process-services-cloud/src/lib/task/task-form/components/task-form-cloud/task-form-cloud.component.scss similarity index 67% rename from lib/process-services-cloud/src/lib/task/task-form/components/task-form-cloud.component.scss rename to lib/process-services-cloud/src/lib/task/task-form/components/task-form-cloud/task-form-cloud.component.scss index 80e7995fcca..3ed08852124 100644 --- a/lib/process-services-cloud/src/lib/task/task-form/components/task-form-cloud.component.scss +++ b/lib/process-services-cloud/src/lib/task/task-form/components/task-form-cloud/task-form-cloud.component.scss @@ -29,23 +29,4 @@ } } } - - &-cloud-spinner { - top: 50%; - left: 50%; - transform: translate(-50%, -50%); - } -} - -adf-cloud-task-form { - .adf-task-form-cloud-spinner { - display: flex; - justify-content: center; - align-items: center; - position: absolute; - top: 50%; - left: 50%; - transform: translate(-50%, -50%); - overflow: hidden; - } } diff --git a/lib/process-services-cloud/src/lib/task/task-form/components/task-form-cloud.component.spec.ts b/lib/process-services-cloud/src/lib/task/task-form/components/task-form-cloud/task-form-cloud.component.spec.ts similarity index 97% rename from lib/process-services-cloud/src/lib/task/task-form/components/task-form-cloud.component.spec.ts rename to lib/process-services-cloud/src/lib/task/task-form/components/task-form-cloud/task-form-cloud.component.spec.ts index cbba6d68888..d383a0f7b3e 100644 --- a/lib/process-services-cloud/src/lib/task/task-form/components/task-form-cloud.component.spec.ts +++ b/lib/process-services-cloud/src/lib/task/task-form/components/task-form-cloud/task-form-cloud.component.spec.ts @@ -20,7 +20,7 @@ import { By } from '@angular/platform-browser'; import { of } from 'rxjs'; import { ComponentFixture, TestBed } from '@angular/core/testing'; import { FORM_FIELD_VALIDATORS, FormModel, FormOutcomeEvent, FormOutcomeModel } from '@alfresco/adf-core'; -import { ProcessServiceCloudTestingModule } from '../../../testing/process-service-cloud.testing.module'; +import { ProcessServiceCloudTestingModule } from '../../../../testing/process-service-cloud.testing.module'; import { TaskFormCloudComponent } from './task-form-cloud.component'; import { TaskDetailsCloudModel, @@ -29,15 +29,15 @@ import { TASK_CREATED_STATE, TASK_RELEASE_PERMISSION, TASK_VIEW_PERMISSION -} from '../../start-task/models/task-details-cloud.model'; -import { TaskCloudService } from '../../services/task-cloud.service'; -import { IdentityUserService } from '../../../people/services/identity-user.service'; +} from '../../../start-task/models/task-details-cloud.model'; +import { TaskCloudService } from '../../../services/task-cloud.service'; +import { IdentityUserService } from '../../../../people/services/identity-user.service'; import { HarnessLoader } from '@angular/cdk/testing'; import { TestbedHarnessEnvironment } from '@angular/cdk/testing/testbed'; import { MatProgressSpinnerHarness } from '@angular/material/progress-spinner/testing'; -import { DisplayModeService } from '../../../form/services/display-mode.service'; -import { FormCloudComponent } from '../../../form/components/form-cloud.component'; -import { MockFormFieldValidator } from '../mocks/task-form-cloud.mock'; +import { DisplayModeService } from '../../../../form/services/display-mode.service'; +import { FormCloudComponent } from '../../../../form/components/form-cloud.component'; +import { MockFormFieldValidator } from '../../mocks/task-form-cloud.mock'; const taskDetails: TaskDetailsCloudModel = { appName: 'simple-app', diff --git a/lib/process-services-cloud/src/lib/task/task-form/components/task-form-cloud.component.stories.ts b/lib/process-services-cloud/src/lib/task/task-form/components/task-form-cloud/task-form-cloud.component.stories.ts similarity index 91% rename from lib/process-services-cloud/src/lib/task/task-form/components/task-form-cloud.component.stories.ts rename to lib/process-services-cloud/src/lib/task/task-form/components/task-form-cloud/task-form-cloud.component.stories.ts index e1d34d1ebcf..91f657f5c47 100644 --- a/lib/process-services-cloud/src/lib/task/task-form/components/task-form-cloud.component.stories.ts +++ b/lib/process-services-cloud/src/lib/task/task-form/components/task-form-cloud/task-form-cloud.component.stories.ts @@ -16,13 +16,13 @@ */ import { applicationConfig, Meta, moduleMetadata, StoryFn } from '@storybook/angular'; -import { FormCloudService } from '../../../form/public-api'; -import { TaskCloudService } from '../../services/task-cloud.service'; -import { TaskFormModule } from '../task-form.module'; +import { FormCloudService } from '../../../../form/public-api'; +import { TaskCloudService } from '../../../services/task-cloud.service'; +import { TaskFormModule } from '../../task-form.module'; import { TaskFormCloudComponent } from './task-form-cloud.component'; -import { TaskCloudServiceMock } from '../../mock/task-cloud.service.mock'; -import { FormCloudServiceMock } from '../../../form/mocks/form-cloud.service.mock'; -import { ProcessServicesCloudStoryModule } from '../../../testing/process-services-cloud-story.module'; +import { TaskCloudServiceMock } from '../../../mock/task-cloud.service.mock'; +import { FormCloudServiceMock } from '../../../../form/mocks/form-cloud.service.mock'; +import { ProcessServicesCloudStoryModule } from '../../../../testing/process-services-cloud-story.module'; import { importProvidersFrom } from '@angular/core'; export default { @@ -37,9 +37,7 @@ export default { ] }), applicationConfig({ - providers: [ - importProvidersFrom(ProcessServicesCloudStoryModule) - ] + providers: [importProvidersFrom(ProcessServicesCloudStoryModule)] }) ], argTypes: { diff --git a/lib/process-services-cloud/src/lib/task/task-form/components/task-form-cloud.component.ts b/lib/process-services-cloud/src/lib/task/task-form/components/task-form-cloud/task-form-cloud.component.ts similarity index 73% rename from lib/process-services-cloud/src/lib/task/task-form/components/task-form-cloud.component.ts rename to lib/process-services-cloud/src/lib/task/task-form/components/task-form-cloud/task-form-cloud.component.ts index fbf86f03843..8c4c3bb7008 100644 --- a/lib/process-services-cloud/src/lib/task/task-form/components/task-form-cloud.component.ts +++ b/lib/process-services-cloud/src/lib/task/task-form/components/task-form-cloud/task-form-cloud.component.ts @@ -15,28 +15,15 @@ * limitations under the License. */ -import { - Component, - DestroyRef, - EventEmitter, - inject, - Input, - OnChanges, - OnInit, - Output, - SimpleChanges, - ViewChild, - ViewEncapsulation -} from '@angular/core'; -import { TaskDetailsCloudModel } from '../../start-task/models/task-details-cloud.model'; -import { TaskCloudService } from '../../services/task-cloud.service'; +import { Component, EventEmitter, Input, OnInit, Output, ViewChild, ViewEncapsulation } from '@angular/core'; +import { TaskDetailsCloudModel } from '../../../start-task/models/task-details-cloud.model'; +import { TaskCloudService } from '../../../services/task-cloud.service'; import { ContentLinkModel, FORM_FIELD_VALIDATORS, FormFieldValidator, FormModel, FormOutcomeEvent, FormRenderingService } from '@alfresco/adf-core'; -import { AttachFileCloudWidgetComponent } from '../../../form/components/widgets/attach-file/attach-file-cloud-widget.component'; -import { DropdownCloudWidgetComponent } from '../../../form/components/widgets/dropdown/dropdown-cloud.widget'; -import { DateCloudWidgetComponent } from '../../../form/components/widgets/date/date-cloud.widget'; -import { FormCloudDisplayModeConfiguration } from '../../../services/form-fields.interfaces'; -import { FormCloudComponent } from '../../../form/components/form-cloud.component'; -import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; +import { AttachFileCloudWidgetComponent } from '../../../../form/components/widgets/attach-file/attach-file-cloud-widget.component'; +import { DropdownCloudWidgetComponent } from '../../../../form/components/widgets/dropdown/dropdown-cloud.widget'; +import { DateCloudWidgetComponent } from '../../../../form/components/widgets/date/date-cloud.widget'; +import { FormCloudDisplayModeConfiguration } from '../../../../services/form-fields.interfaces'; +import { FormCloudComponent } from '../../../../form/components/form-cloud.component'; @Component({ selector: 'adf-cloud-task-form', @@ -44,11 +31,17 @@ import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; styleUrls: ['./task-form-cloud.component.scss'], encapsulation: ViewEncapsulation.None }) -export class TaskFormCloudComponent implements OnInit, OnChanges { +export class TaskFormCloudComponent implements OnInit { /** App id to fetch corresponding form and values. */ @Input() appName: string = ''; + /**Candidates user and groups */ + @Input() + candidateUsers: string[] = []; + @Input() + candidateGroups: string[] = []; + /** Task id to fetch corresponding form and values. */ @Input() taskId: string; @@ -87,6 +80,10 @@ export class TaskFormCloudComponent implements OnInit, OnChanges { @Input() fieldValidators: FormFieldValidator[]; + /** Task details. */ + @Input() + taskDetails: TaskDetailsCloudModel; + /** Emitted when the form is saved. */ @Output() formSaved = new EventEmitter(); @@ -126,12 +123,6 @@ export class TaskFormCloudComponent implements OnInit, OnChanges { @Output() executeOutcome = new EventEmitter(); - /** - * Emitted when a task is loaded`. - */ - @Output() - onTaskLoaded = new EventEmitter(); /* eslint-disable-line */ - /** Emitted when a display mode configuration is turned on. */ @Output() displayModeOn = new EventEmitter(); @@ -143,15 +134,8 @@ export class TaskFormCloudComponent implements OnInit, OnChanges { @ViewChild('adfCloudForm', { static: false }) adfCloudForm: FormCloudComponent; - taskDetails: TaskDetailsCloudModel; - - candidateUsers: string[] = []; - candidateGroups: string[] = []; - loading: boolean = false; - private readonly destroyRef = inject(DestroyRef); - constructor(private taskCloudService: TaskCloudService, private formRenderingService: FormRenderingService) { this.formRenderingService.setComponentTypeResolver('upload', () => AttachFileCloudWidgetComponent, true); this.formRenderingService.setComponentTypeResolver('dropdown', () => DropdownCloudWidgetComponent, true); @@ -160,46 +144,12 @@ export class TaskFormCloudComponent implements OnInit, OnChanges { ngOnInit() { this.initFieldValidators(); - - if (this.appName === '' && this.taskId) { - this.loadTask(); - } - } - - ngOnChanges(changes: SimpleChanges) { - const appName = changes['appName']; - if (appName && appName.currentValue !== appName.previousValue && this.taskId) { - this.loadTask(); - return; - } - - const taskId = changes['taskId']; - if (taskId?.currentValue && this.appName) { - this.loadTask(); - return; - } } private initFieldValidators() { this.fieldValidators = this.fieldValidators ? [...FORM_FIELD_VALIDATORS, ...this.fieldValidators] : [...FORM_FIELD_VALIDATORS]; } - private loadTask() { - this.loading = true; - this.taskCloudService - .getTaskById(this.appName, this.taskId) - .pipe(takeUntilDestroyed(this.destroyRef)) - .subscribe((details) => { - this.taskDetails = details; - this.loading = false; - this.onTaskLoaded.emit(this.taskDetails); - }); - - this.taskCloudService.getCandidateUsers(this.appName, this.taskId).subscribe((users) => (this.candidateUsers = users || [])); - - this.taskCloudService.getCandidateGroups(this.appName, this.taskId).subscribe((groups) => (this.candidateGroups = groups || [])); - } - hasForm(): boolean { return this.taskDetails && !!this.taskDetails.formKey; } @@ -233,17 +183,14 @@ export class TaskFormCloudComponent implements OnInit, OnChanges { } onCompleteTask() { - this.loadTask(); this.taskCompleted.emit(this.taskId); } onClaimTask() { - this.loadTask(); this.taskClaimed.emit(this.taskId); } onUnclaimTask() { - this.loadTask(); this.taskUnclaimed.emit(this.taskId); } diff --git a/lib/process-services-cloud/src/lib/task/task-form/components/user-task-cloud-buttons/user-task-cloud-buttons.component.html b/lib/process-services-cloud/src/lib/task/task-form/components/user-task-cloud-buttons/user-task-cloud-buttons.component.html new file mode 100644 index 00000000000..f5821a8a76d --- /dev/null +++ b/lib/process-services-cloud/src/lib/task/task-form/components/user-task-cloud-buttons/user-task-cloud-buttons.component.html @@ -0,0 +1,27 @@ + + + diff --git a/lib/process-services-cloud/src/lib/task/task-form/components/user-task-cloud-buttons/user-task-cloud-buttons.component.scss b/lib/process-services-cloud/src/lib/task/task-form/components/user-task-cloud-buttons/user-task-cloud-buttons.component.scss new file mode 100644 index 00000000000..d16f66ad282 --- /dev/null +++ b/lib/process-services-cloud/src/lib/task/task-form/components/user-task-cloud-buttons/user-task-cloud-buttons.component.scss @@ -0,0 +1,3 @@ +div { + background-color: inherit; +} diff --git a/lib/process-services-cloud/src/lib/task/task-form/components/user-task-cloud-buttons/user-task-cloud-buttons.component.spec.ts b/lib/process-services-cloud/src/lib/task/task-form/components/user-task-cloud-buttons/user-task-cloud-buttons.component.spec.ts new file mode 100644 index 00000000000..306ca2ffc5c --- /dev/null +++ b/lib/process-services-cloud/src/lib/task/task-form/components/user-task-cloud-buttons/user-task-cloud-buttons.component.spec.ts @@ -0,0 +1,37 @@ +/*! + * @license + * Copyright © 2005-2024 Hyland Software, Inc. and its affiliates. All rights reserved. + * + * 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 { UserTaskCloudButtonsComponent } from './user-task-cloud-buttons.component'; + +describe('UserTaskCloudButtonsComponent', () => { + let component: UserTaskCloudButtonsComponent; + let fixture: ComponentFixture; + + beforeEach(() => { + TestBed.configureTestingModule({ + imports: [UserTaskCloudButtonsComponent] + }); + fixture = TestBed.createComponent(UserTaskCloudButtonsComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/lib/process-services-cloud/src/lib/task/task-form/components/user-task-cloud-buttons/user-task-cloud-buttons.component.ts b/lib/process-services-cloud/src/lib/task/task-form/components/user-task-cloud-buttons/user-task-cloud-buttons.component.ts new file mode 100644 index 00000000000..c2b2850a735 --- /dev/null +++ b/lib/process-services-cloud/src/lib/task/task-form/components/user-task-cloud-buttons/user-task-cloud-buttons.component.ts @@ -0,0 +1,71 @@ +/*! + * @license + * Copyright © 2005-2024 Hyland Software, Inc. and its affiliates. All rights reserved. + * + * 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, Output } from '@angular/core'; + +@Component({ + selector: 'adf-cloud-user-task-cloud-buttons', + templateUrl: './user-task-cloud-buttons.component.html', + styleUrls: ['./user-task-cloud-buttons.component.scss'] +}) +export class UserTaskCloudButtonsComponent { + /** App id to fetch corresponding form and values. */ + @Input() + appName: string = ''; + + @Input() + canClaimTask: boolean; + + @Input() + canUnclaimTask: boolean; + + /** Task id to fetch corresponding form and values. */ + @Input() + taskId: string; + + /** Toggle rendering of the `Cancel` button. */ + @Input() + showCancelButton = true; + + /** Emitted when any error occurs. */ + @Output() error = new EventEmitter(); + + /** Emitted when the cancel button is clicked. */ + @Output() cancelClick = new EventEmitter(); + + /** Emitted when the task is claimed. */ + @Output() claimTask = new EventEmitter(); + + /** Emitted when the task is unclaimed. */ + @Output() unclaimTask = new EventEmitter(); + + onError(data: any): void { + this.error.emit(data); + } + + onUnclaimTask(): void { + this.unclaimTask.emit(); + } + + onClaimTask(): void { + this.claimTask.emit(); + } + + onCancelClick(): void { + this.cancelClick.emit(); + } +} diff --git a/lib/process-services-cloud/src/lib/task/task-form/components/user-task-cloud/user-task-cloud.component.html b/lib/process-services-cloud/src/lib/task/task-form/components/user-task-cloud/user-task-cloud.component.html new file mode 100644 index 00000000000..384f0240d2e --- /dev/null +++ b/lib/process-services-cloud/src/lib/task/task-form/components/user-task-cloud/user-task-cloud.component.html @@ -0,0 +1,88 @@ +
+
+ + + + + + + + + + + + + + +

+ + {{ taskDetails?.name || 'FORM.FORM_RENDERER.NAMELESS_TASK' | translate }} + +

+
+
+ + + + + + + + +
+
+ +
+
+
+ + + + + + + + + diff --git a/lib/process-services-cloud/src/lib/task/task-form/components/user-task-cloud/user-task-cloud.component.scss b/lib/process-services-cloud/src/lib/task/task-form/components/user-task-cloud/user-task-cloud.component.scss new file mode 100644 index 00000000000..a30d10d50ed --- /dev/null +++ b/lib/process-services-cloud/src/lib/task/task-form/components/user-task-cloud/user-task-cloud.component.scss @@ -0,0 +1,10 @@ +.adf-task-form-cloud-container { + width: 100%; + height: 100%; +} + +.adf-user-task-cloud-spinner { + top: 50%; + left: 50%; + transform: translate(-50%, -50%); +} diff --git a/lib/process-services-cloud/src/lib/task/task-form/components/user-task-cloud/user-task-cloud.component.spec.ts b/lib/process-services-cloud/src/lib/task/task-form/components/user-task-cloud/user-task-cloud.component.spec.ts new file mode 100644 index 00000000000..783ea62a73e --- /dev/null +++ b/lib/process-services-cloud/src/lib/task/task-form/components/user-task-cloud/user-task-cloud.component.spec.ts @@ -0,0 +1,37 @@ +/*! + * @license + * Copyright © 2005-2024 Hyland Software, Inc. and its affiliates. All rights reserved. + * + * 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 { UserTaskCloudComponent } from './user-task-cloud.component'; + +describe('UserTaskCloudComponent', () => { + let component: UserTaskCloudComponent; + let fixture: ComponentFixture; + + beforeEach(() => { + TestBed.configureTestingModule({ + imports: [UserTaskCloudComponent] + }); + fixture = TestBed.createComponent(UserTaskCloudComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/lib/process-services-cloud/src/lib/task/task-form/components/user-task-cloud/user-task-cloud.component.ts b/lib/process-services-cloud/src/lib/task/task-form/components/user-task-cloud/user-task-cloud.component.ts new file mode 100644 index 00000000000..fdeb75e3913 --- /dev/null +++ b/lib/process-services-cloud/src/lib/task/task-form/components/user-task-cloud/user-task-cloud.component.ts @@ -0,0 +1,250 @@ +/*! + * @license + * Copyright © 2005-2024 Hyland Software, Inc. and its affiliates. All rights reserved. + * + * 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 { ContentLinkModel, FormFieldValidator, FormModel, FormOutcomeEvent } from '@alfresco/adf-core'; +import { Component, DestroyRef, EventEmitter, inject, Input, OnChanges, OnInit, Output, SimpleChanges, ViewChild } from '@angular/core'; +import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; +import { FormCloudDisplayModeConfiguration } from '../../../../services/form-fields.interfaces'; +import { TaskCloudService } from '../../../services/task-cloud.service'; +import { TaskDetailsCloudModel } from '../../../start-task/models/task-details-cloud.model'; +import { TaskFormCloudComponent } from '@alfresco/adf-process-services-cloud'; + +const TaskTypes = { + Form: 'form', + Screen: 'screen', + None: '' +} as const; + +type TaskTypesType = (typeof TaskTypes)[keyof typeof TaskTypes]; + +@Component({ + selector: 'adf-cloud-user-task', + templateUrl: './user-task-cloud.component.html', + styleUrls: ['./user-task-cloud.component.scss'] +}) +export class UserTaskCloudComponent implements OnInit, OnChanges { + @ViewChild('adfCloudTaskForm') + adfCloudTaskForm: TaskFormCloudComponent; + + /** App id to fetch corresponding form and values. */ + @Input() + appName: string = ''; + + /** The available display configurations for the form */ + @Input() + displayModeConfigurations: FormCloudDisplayModeConfiguration[]; + + /** FormFieldValidator allow to provide additional validators to the form field. */ + @Input() + fieldValidators: FormFieldValidator[]; + + /** Toggle readonly state of the task. */ + @Input() + readOnly = false; + + /** Toggle rendering of the `Cancel` button. */ + @Input() + showCancelButton = true; + + /** Toggle rendering of the `Complete` button. */ + @Input() + showCompleteButton = true; + + /** Toggle rendering of the form title. */ + @Input() + showTitle: boolean = true; + + /** Toggle rendering of the `Validation` icon. */ + @Input() + showValidationIcon = true; + + /** Task id to fetch corresponding form and values. */ + @Input() + taskId: string; + + /** Emitted when the cancel button is clicked. */ + @Output() + cancelClick = new EventEmitter(); + + /** Emitted when any error occurs. */ + @Output() + error = new EventEmitter(); + + /** + * Emitted when any outcome is executed. Default behaviour can be prevented + * via `event.preventDefault()`. + */ + @Output() + executeOutcome = new EventEmitter(); + + /** Emitted when form content is clicked. */ + @Output() + formContentClicked: EventEmitter = new EventEmitter(); + + /** Emitted when the form is saved. */ + @Output() + formSaved = new EventEmitter(); + + /** + * Emitted when a task is loaded`. + */ + @Output() + onTaskLoaded = new EventEmitter(); /* eslint-disable-line */ + + /** Emitted when the task is claimed. */ + @Output() + taskClaimed = new EventEmitter(); + + /** Emitted when the task is unclaimed. */ + @Output() + taskUnclaimed = new EventEmitter(); + + /** Emitted when the task is completed. */ + @Output() + taskCompleted = new EventEmitter(); + + candidateUsers: string[] = []; + candidateGroups: string[] = []; + loading: boolean = false; + taskDetails: TaskDetailsCloudModel; + taskType: TaskTypesType; + taskTypeEnum = TaskTypes; + + private taskCloudService: TaskCloudService = inject(TaskCloudService); + private readonly destroyRef = inject(DestroyRef); + + canClaimTask(): boolean { + return !this.readOnly && this.taskCloudService.canClaimTask(this.taskDetails) && this.hasCandidateUsersOrGroups(); + } + + canCompleteTask(): boolean { + return this.showCompleteButton && !this.readOnly && this.taskCloudService.canCompleteTask(this.taskDetails); + } + + canUnclaimTask(): boolean { + return !this.readOnly && this.taskCloudService.canUnclaimTask(this.taskDetails) && this.hasCandidateUsersOrGroups(); + } + + getTaskType() { + if (this.taskDetails && !!this.taskDetails.formKey && this.taskDetails.formKey.includes(this.taskTypeEnum.Form)) { + this.taskType = this.taskTypeEnum.Form; + } else if (this.taskDetails && !!this.taskDetails.formKey && this.taskDetails.formKey.includes(this.taskTypeEnum.Screen)) { + this.taskType = this.taskTypeEnum.Screen; + } else { + this.taskType = this.taskTypeEnum.None; + } + } + + hasCandidateUsers(): boolean { + return this.candidateUsers.length !== 0; + } + + hasCandidateGroups(): boolean { + return this.candidateGroups.length !== 0; + } + + hasCandidateUsersOrGroups(): boolean { + return this.hasCandidateUsers() || this.hasCandidateGroups(); + } + + onCancelForm() { + this.cancelClick.emit(); + } + + onCancelClick() { + this.cancelClick.emit(this.taskId); + } + + onClaimTask() { + this.loadTask(); + this.taskClaimed.emit(this.taskId); + } + + onCompleteTask() { + this.loadTask(); + this.taskCompleted.emit(this.taskId); + } + + onCompleteTaskForm() { + this.taskCompleted.emit(); + } + + onError(data: any) { + this.error.emit(data); + } + + onExecuteOutcome(outcome: FormOutcomeEvent) { + this.executeOutcome.emit(outcome); + } + onFormContentClicked(content: ContentLinkModel) { + this.formContentClicked.emit(content); + } + onFormSaved() { + this.formSaved.emit(); + } + + onTaskUnclaimed() { + this.taskUnclaimed.emit(); + } + + onUnclaimTask() { + this.loadTask(); + this.taskUnclaimed.emit(this.taskId); + } + + private loadTask() { + this.loading = true; + this.taskCloudService + .getTaskById(this.appName, this.taskId) + .pipe(takeUntilDestroyed(this.destroyRef)) + .subscribe((details) => { + this.taskDetails = details; + this.getTaskType(); + this.loading = false; + this.onTaskLoaded.emit(this.taskDetails); + }); + + this.taskCloudService.getCandidateUsers(this.appName, this.taskId).subscribe((users) => (this.candidateUsers = users || [])); + this.taskCloudService.getCandidateGroups(this.appName, this.taskId).subscribe((groups) => (this.candidateGroups = groups || [])); + } + + public switchToDisplayMode(newDisplayMode?: string) { + if (this.adfCloudTaskForm) { + this.adfCloudTaskForm.switchToDisplayMode(newDisplayMode); + } + } + + ngOnChanges(changes: SimpleChanges) { + const appName = changes['appName']; + if (appName && appName.currentValue !== appName.previousValue && this.taskId) { + this.loadTask(); + return; + } + + const taskId = changes['taskId']; + if (taskId?.currentValue && this.appName) { + this.loadTask(); + return; + } + } + + ngOnInit() { + if (this.appName === '' && this.taskId) { + this.loadTask(); + } + } +} diff --git a/lib/process-services-cloud/src/lib/task/task-form/components/user-task-cloud/user-task-cloud.interface.ts b/lib/process-services-cloud/src/lib/task/task-form/components/user-task-cloud/user-task-cloud.interface.ts new file mode 100644 index 00000000000..773bffde36a --- /dev/null +++ b/lib/process-services-cloud/src/lib/task/task-form/components/user-task-cloud/user-task-cloud.interface.ts @@ -0,0 +1,26 @@ +/*! + * @license + * Copyright © 2005-2024 Hyland Software, Inc. and its affiliates. All rights reserved. + * + * 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 { EventEmitter } from '@angular/core'; + +export interface UserTaskCustomUi { + error: EventEmitter; + cancelClick: EventEmitter; + taskClaimed: EventEmitter; + taskUnclaimed: EventEmitter; + taskCompleted: EventEmitter; +} diff --git a/lib/process-services-cloud/src/lib/task/task-form/public-api.ts b/lib/process-services-cloud/src/lib/task/task-form/public-api.ts index b75729a7716..ed10ea7e25d 100644 --- a/lib/process-services-cloud/src/lib/task/task-form/public-api.ts +++ b/lib/process-services-cloud/src/lib/task/task-form/public-api.ts @@ -15,6 +15,7 @@ * limitations under the License. */ -export * from './components/task-form-cloud.component'; +export * from './components/task-form-cloud/task-form-cloud.component'; +export * from './components/user-task-cloud/user-task-cloud.component'; export * from './task-form.module'; diff --git a/lib/process-services-cloud/src/lib/task/task-form/task-form.module.ts b/lib/process-services-cloud/src/lib/task/task-form/task-form.module.ts index 4a23edf8199..bd3a42e4598 100644 --- a/lib/process-services-cloud/src/lib/task/task-form/task-form.module.ts +++ b/lib/process-services-cloud/src/lib/task/task-form/task-form.module.ts @@ -21,22 +21,15 @@ 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 { TaskFormCloudComponent } from './components/task-form-cloud/task-form-cloud.component'; import { CoreModule } from '@alfresco/adf-core'; +import { ScreenCloudComponent } from '../../screen/components/screen-cloud/screen-cloud.component'; +import { UserTaskCloudComponent } from './components/user-task-cloud/user-task-cloud.component'; +import { UserTaskCloudButtonsComponent } from './components/user-task-cloud-buttons/user-task-cloud-buttons.component'; @NgModule({ - imports: [ - CoreModule, - CommonModule, - MaterialModule, - FormCloudModule, - TaskDirectiveModule - ], - declarations: [ - TaskFormCloudComponent - ], - exports: [ - TaskFormCloudComponent - ] + imports: [CoreModule, CommonModule, MaterialModule, FormCloudModule, TaskDirectiveModule, ScreenCloudComponent], + declarations: [TaskFormCloudComponent, UserTaskCloudComponent, UserTaskCloudButtonsComponent], + exports: [TaskFormCloudComponent, UserTaskCloudComponent] }) -export class TaskFormModule { } +export class TaskFormModule {} From f8d4e2c256bad8c3971cdb17d18fe30643d8ebc1 Mon Sep 17 00:00:00 2001 From: "tomek.hanaj" Date: Wed, 11 Dec 2024 12:49:42 +0100 Subject: [PATCH 2/8] updated unit tests --- .../screen-cloud.component.spec.ts | 25 +- .../screen-cloud/screen-cloud.component.ts | 19 +- .../src/lib/services/public-api.ts | 9 +- .../services/screen-rendering.service.spec.ts | 32 ++ .../lib/services/screen-rendering.service.ts | 24 + .../task-form-cloud.component.html | 27 +- .../task-form-cloud.component.spec.ts | 320 ++----------- .../task-form-cloud.component.ts | 22 +- .../user-task-cloud-buttons.component.html | 2 + .../user-task-cloud-buttons.component.spec.ts | 99 +++- .../user-task-cloud-buttons.component.ts | 3 +- .../user-task-cloud.component.html | 6 +- .../user-task-cloud.component.scss | 7 +- .../user-task-cloud.component.spec.ts | 440 +++++++++++++++++- .../user-task-cloud.component.ts | 3 + .../lib/task/task-form/task-form.module.ts | 1 - 16 files changed, 710 insertions(+), 329 deletions(-) create mode 100644 lib/process-services-cloud/src/lib/services/screen-rendering.service.spec.ts create mode 100644 lib/process-services-cloud/src/lib/services/screen-rendering.service.ts diff --git a/lib/process-services-cloud/src/lib/screen/components/screen-cloud/screen-cloud.component.spec.ts b/lib/process-services-cloud/src/lib/screen/components/screen-cloud/screen-cloud.component.spec.ts index c9dc04ae55f..5014b4a1ce3 100644 --- a/lib/process-services-cloud/src/lib/screen/components/screen-cloud/screen-cloud.component.spec.ts +++ b/lib/process-services-cloud/src/lib/screen/components/screen-cloud/screen-cloud.component.spec.ts @@ -18,21 +18,36 @@ import { ComponentFixture, TestBed } from '@angular/core/testing'; import { ScreenCloudComponent } from './screen-cloud.component'; +import { Component } from '@angular/core'; +import { CommonModule } from '@angular/common'; +import { ScreenRenderingService } from '../../../services/public-api'; +import { By } from '@angular/platform-browser'; + +@Component({ + selector: 'adf-cloud-test-component', + template: `
test component
`, + imports: [CommonModule], + standalone: true +}) +class TestComponent {} describe('ScreenCloudComponent', () => { - let component: ScreenCloudComponent; let fixture: ComponentFixture; + let screenRenderingService: ScreenRenderingService; beforeEach(() => { TestBed.configureTestingModule({ - imports: [ScreenCloudComponent] + imports: [ScreenCloudComponent, TestComponent] }); fixture = TestBed.createComponent(ScreenCloudComponent); - component = fixture.componentInstance; + screenRenderingService = TestBed.inject(ScreenRenderingService); + screenRenderingService.register({ ['test']: () => TestComponent }); + fixture.componentRef.setInput('screenId', 'test'); fixture.detectChanges(); }); - it('should create', () => { - expect(component).toBeTruthy(); + it('should create custom component instance', () => { + const dynamicComponent = fixture.debugElement.query(By.css('.adf-cloud-test-container')); + expect(dynamicComponent).toBeTruthy(); }); }); diff --git a/lib/process-services-cloud/src/lib/screen/components/screen-cloud/screen-cloud.component.ts b/lib/process-services-cloud/src/lib/screen/components/screen-cloud/screen-cloud.component.ts index e50e0e8274f..1684fe789ce 100644 --- a/lib/process-services-cloud/src/lib/screen/components/screen-cloud/screen-cloud.component.ts +++ b/lib/process-services-cloud/src/lib/screen/components/screen-cloud/screen-cloud.component.ts @@ -15,8 +15,8 @@ * limitations under the License. */ -// FormRenderingService, -import { DynamicComponentModel, ScreenRenderingService } from '@alfresco/adf-core'; +import { DynamicComponentModel } from '@alfresco/adf-core'; +import { ScreenRenderingService } from '../../../services/public-api'; import { CommonModule } from '@angular/common'; import { Component, ComponentRef, inject, Input, OnInit, ViewChild, ViewContainerRef } from '@angular/core'; @@ -24,8 +24,7 @@ import { Component, ComponentRef, inject, Input, OnInit, ViewChild, ViewContaine selector: 'adf-cloud-screen-cloud', standalone: true, imports: [CommonModule], - templateUrl: './screen-cloud.component.html', - styleUrls: ['./screen-cloud.component.scss'] + template: '
' }) export class ScreenCloudComponent implements OnInit { /** Task id to fetch corresponding form and values. */ @@ -33,19 +32,25 @@ export class ScreenCloudComponent implements OnInit { /** App id to fetch corresponding form and values. */ @Input() appName: string = ''; + /** Screen id to fetch corresponding screen widget. */ + @Input() + screenId: string = ''; /** Toggle readonly state of the task. */ @Input() readOnly = false; @ViewChild('container', { read: ViewContainerRef, static: true }) container: ViewContainerRef; - screenComponent: DynamicComponentModel = { type: 'screen-one' }; + screenComponent: DynamicComponentModel; componentRef: ComponentRef; private readonly screenRenderingService = inject(ScreenRenderingService); ngOnInit() { - const componentType = this.screenRenderingService.resolveComponentType(this.screenComponent); - this.componentRef = this.container.createComponent(componentType); + if (this.screenId) { + this.screenComponent = { type: this.screenId }; + const componentType = this.screenRenderingService.resolveComponentType(this.screenComponent); + this.componentRef = this.container.createComponent(componentType); + } } } diff --git a/lib/process-services-cloud/src/lib/services/public-api.ts b/lib/process-services-cloud/src/lib/services/public-api.ts index 40684e03a66..f07bd965135 100644 --- a/lib/process-services-cloud/src/lib/services/public-api.ts +++ b/lib/process-services-cloud/src/lib/services/public-api.ts @@ -15,13 +15,14 @@ * limitations under the License. */ -export * from './user-preference-cloud.service'; -export * from './local-preference-cloud.service'; +export * from './base-cloud.service'; export * from './cloud-token.service'; +export * from './form-fields.interfaces'; +export * from './local-preference-cloud.service'; export * from './notification-cloud.service'; export * from './preference-cloud.interface'; -export * from './form-fields.interfaces'; -export * from './base-cloud.service'; +export * from './screen-rendering.service'; export * from './task-list-cloud.service.interface'; +export * from './user-preference-cloud.service'; export * from './variable-mapper.sevice'; export * from './web-socket.service'; diff --git a/lib/process-services-cloud/src/lib/services/screen-rendering.service.spec.ts b/lib/process-services-cloud/src/lib/services/screen-rendering.service.spec.ts new file mode 100644 index 00000000000..542c61a872b --- /dev/null +++ b/lib/process-services-cloud/src/lib/services/screen-rendering.service.spec.ts @@ -0,0 +1,32 @@ +/*! + * @license + * Copyright © 2005-2024 Hyland Software, Inc. and its affiliates. All rights reserved. + * + * 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 { TestBed } from '@angular/core/testing'; +import { ScreenRenderingService } from './screen-rendering.service'; + +describe('ScreenRenderingService', () => { + let service: ScreenRenderingService; + + beforeEach(() => { + TestBed.configureTestingModule({}); + service = TestBed.inject(ScreenRenderingService); + }); + + it('should be created', () => { + expect(service).toBeTruthy(); + }); +}); diff --git a/lib/process-services-cloud/src/lib/services/screen-rendering.service.ts b/lib/process-services-cloud/src/lib/services/screen-rendering.service.ts new file mode 100644 index 00000000000..7cc13efe0ea --- /dev/null +++ b/lib/process-services-cloud/src/lib/services/screen-rendering.service.ts @@ -0,0 +1,24 @@ +/*! + * @license + * Copyright © 2005-2024 Hyland Software, Inc. and its affiliates. All rights reserved. + * + * 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 { DynamicComponentMapper } from '../../../../core/src/lib/common/services/dynamic-component-mapper.service'; + +@Injectable({ + providedIn: 'root' +}) +export class ScreenRenderingService extends DynamicComponentMapper {} diff --git a/lib/process-services-cloud/src/lib/task/task-form/components/task-form-cloud/task-form-cloud.component.html b/lib/process-services-cloud/src/lib/task/task-form/components/task-form-cloud/task-form-cloud.component.html index 3692ce7d20c..25d27e255f4 100644 --- a/lib/process-services-cloud/src/lib/task/task-form/components/task-form-cloud/task-form-cloud.component.html +++ b/lib/process-services-cloud/src/lib/task/task-form/components/task-form-cloud/task-form-cloud.component.html @@ -21,22 +21,17 @@ (displayModeOn)="onDisplayModeOn($event)" (displayModeOff)="onDisplayModeOff($event)"> - - + + - - - - - diff --git a/lib/process-services-cloud/src/lib/task/task-form/components/task-form-cloud/task-form-cloud.component.spec.ts b/lib/process-services-cloud/src/lib/task/task-form/components/task-form-cloud/task-form-cloud.component.spec.ts index d383a0f7b3e..4d9a9f3be20 100644 --- a/lib/process-services-cloud/src/lib/task/task-form/components/task-form-cloud/task-form-cloud.component.spec.ts +++ b/lib/process-services-cloud/src/lib/task/task-form/components/task-form-cloud/task-form-cloud.component.spec.ts @@ -15,32 +15,30 @@ * limitations under the License. */ -import { DebugElement, SimpleChange } from '@angular/core'; -import { By } from '@angular/platform-browser'; -import { of } from 'rxjs'; -import { ComponentFixture, TestBed } from '@angular/core/testing'; import { FORM_FIELD_VALIDATORS, FormModel, FormOutcomeEvent, FormOutcomeModel } from '@alfresco/adf-core'; +import { FormCustomOutcomesComponent } from '@alfresco/adf-process-services-cloud'; +import { ComponentFixture, TestBed } from '@angular/core/testing'; +import { of } from 'rxjs'; +import { FormCloudComponent } from '../../../../form/components/form-cloud.component'; +import { DisplayModeService } from '../../../../form/services/display-mode.service'; +import { IdentityUserService } from '../../../../people/services/identity-user.service'; import { ProcessServiceCloudTestingModule } from '../../../../testing/process-service-cloud.testing.module'; -import { TaskFormCloudComponent } from './task-form-cloud.component'; +import { TaskCloudService } from '../../../services/task-cloud.service'; import { - TaskDetailsCloudModel, TASK_ASSIGNED_STATE, TASK_CLAIM_PERMISSION, TASK_CREATED_STATE, TASK_RELEASE_PERMISSION, - TASK_VIEW_PERMISSION + TASK_VIEW_PERMISSION, + TaskDetailsCloudModel } from '../../../start-task/models/task-details-cloud.model'; -import { TaskCloudService } from '../../../services/task-cloud.service'; -import { IdentityUserService } from '../../../../people/services/identity-user.service'; -import { HarnessLoader } from '@angular/cdk/testing'; -import { TestbedHarnessEnvironment } from '@angular/cdk/testing/testbed'; -import { MatProgressSpinnerHarness } from '@angular/material/progress-spinner/testing'; -import { DisplayModeService } from '../../../../form/services/display-mode.service'; -import { FormCloudComponent } from '../../../../form/components/form-cloud.component'; import { MockFormFieldValidator } from '../../mocks/task-form-cloud.mock'; +import { UserTaskCloudButtonsComponent } from '../user-task-cloud-buttons/user-task-cloud-buttons.component'; +import { TaskFormCloudComponent } from './task-form-cloud.component'; const taskDetails: TaskDetailsCloudModel = { appName: 'simple-app', + appVersion: 1, assignee: 'admin.adf', completedDate: null, createdDate: new Date(1555419255340), @@ -55,21 +53,16 @@ const taskDetails: TaskDetailsCloudModel = { }; describe('TaskFormCloudComponent', () => { - let loader: HarnessLoader; let taskCloudService: TaskCloudService; let identityUserService: IdentityUserService; - - let getTaskSpy: jasmine.Spy; let getCurrentUserSpy: jasmine.Spy; - let debugElement: DebugElement; - let component: TaskFormCloudComponent; let fixture: ComponentFixture; beforeEach(() => { TestBed.configureTestingModule({ imports: [ProcessServiceCloudTestingModule], - declarations: [FormCloudComponent] + declarations: [FormCloudComponent, UserTaskCloudButtonsComponent, FormCustomOutcomesComponent] }); taskDetails.status = TASK_ASSIGNED_STATE; taskDetails.permissions = [TASK_VIEW_PERMISSION]; @@ -78,215 +71,127 @@ describe('TaskFormCloudComponent', () => { identityUserService = TestBed.inject(IdentityUserService); getCurrentUserSpy = spyOn(identityUserService, 'getCurrentUserInfo').and.returnValue({ username: 'admin.adf' }); taskCloudService = TestBed.inject(TaskCloudService); - getTaskSpy = spyOn(taskCloudService, 'getTaskById').and.returnValue(of(taskDetails)); - spyOn(taskCloudService, 'getCandidateGroups').and.returnValue(of([])); - spyOn(taskCloudService, 'getCandidateUsers').and.returnValue(of([])); - fixture = TestBed.createComponent(TaskFormCloudComponent); - debugElement = fixture.debugElement; component = fixture.componentInstance; - loader = TestbedHarnessEnvironment.loader(fixture); }); afterEach(() => { fixture.destroy(); }); - describe('Complete button', () => { - beforeEach(() => { - component.taskId = 'task1'; - component.ngOnChanges({ appName: new SimpleChange(null, 'app1', false) }); - fixture.detectChanges(); - }); - - it('should show complete button when status is ASSIGNED', () => { - const completeBtn = debugElement.query(By.css('[adf-cloud-complete-task]')); - expect(completeBtn.nativeElement).toBeDefined(); - expect(completeBtn.nativeElement.innerText.trim()).toEqual('ADF_CLOUD_TASK_FORM.EMPTY_FORM.BUTTONS.COMPLETE'); - }); - - it('should not show complete button when status is ASSIGNED but assigned to a different person', () => { - getCurrentUserSpy.and.returnValue({}); - fixture.detectChanges(); - - const completeBtn = debugElement.query(By.css('[adf-cloud-complete-task]')); - expect(completeBtn).toBeNull(); - }); - - it('should not show complete button when showCompleteButton=false', () => { - component.showCompleteButton = false; - fixture.detectChanges(); - - const completeBtn = debugElement.query(By.css('[adf-cloud-complete-task]')); - expect(completeBtn).toBeNull(); - }); - }); - describe('Claim/Unclaim buttons', () => { beforeEach(() => { spyOn(component, 'hasCandidateUsers').and.returnValue(true); - getTaskSpy.and.returnValue(of(taskDetails)); + fixture.componentRef.setInput('taskDetails', taskDetails); component.taskId = 'task1'; - component.ngOnChanges({ appName: new SimpleChange(null, 'app1', false) }); + component.showCancelButton = true; fixture.detectChanges(); }); it('should not show release button for standalone task', () => { taskDetails.permissions = [TASK_RELEASE_PERMISSION]; taskDetails.standalone = true; - getTaskSpy.and.returnValue(of(taskDetails)); fixture.detectChanges(); + const canUnclaimTask = component.canUnclaimTask(); - const unclaimBtn = debugElement.query(By.css('[adf-cloud-unclaim-task]')); - expect(unclaimBtn).toBeNull(); + expect(canUnclaimTask).toBe(false); }); it('should not show claim button for standalone task', () => { taskDetails.status = TASK_CREATED_STATE; taskDetails.permissions = [TASK_CLAIM_PERMISSION]; taskDetails.standalone = true; - getTaskSpy.and.returnValue(of(taskDetails)); fixture.detectChanges(); + const canClaimTask = component.canClaimTask(); - const claimBtn = debugElement.query(By.css('[adf-cloud-claim-task]')); - expect(claimBtn).toBeNull(); + expect(canClaimTask).toBe(false); }); it('should show release button when task is assigned to one of the candidate users', () => { taskDetails.permissions = [TASK_RELEASE_PERMISSION]; fixture.detectChanges(); + const canUnclaimTask = component.canUnclaimTask(); - const unclaimBtn = debugElement.query(By.css('[adf-cloud-unclaim-task]')); - expect(unclaimBtn.nativeElement).toBeDefined(); - expect(unclaimBtn.nativeElement.innerText.trim()).toEqual('ADF_CLOUD_TASK_FORM.EMPTY_FORM.BUTTONS.UNCLAIM'); + expect(canUnclaimTask).toBe(true); }); it('should not show unclaim button when status is ASSIGNED but assigned to different person', () => { getCurrentUserSpy.and.returnValue({}); fixture.detectChanges(); + const canUnclaimTask = component.canUnclaimTask(); - const unclaimBtn = debugElement.query(By.css('[adf-cloud-unclaim-task]')); - expect(unclaimBtn).toBeNull(); + expect(canUnclaimTask).toBe(false); }); it('should not show unclaim button when status is not ASSIGNED', () => { taskDetails.status = undefined; fixture.detectChanges(); + const canUnclaimTask = component.canUnclaimTask(); - const unclaimBtn = debugElement.query(By.css('[adf-cloud-unclaim-task]')); - expect(unclaimBtn).toBeNull(); + expect(canUnclaimTask).toBe(false); }); it('should not show unclaim button when status is ASSIGNED and permissions not include RELEASE', () => { taskDetails.status = TASK_ASSIGNED_STATE; taskDetails.permissions = [TASK_VIEW_PERMISSION]; fixture.detectChanges(); + const canUnclaimTask = component.canUnclaimTask(); - const unclaimBtn = debugElement.query(By.css('[adf-cloud-unclaim-task]')); - expect(unclaimBtn).toBeNull(); + expect(canUnclaimTask).toBe(false); }); it('should show claim button when status is CREATED and permission includes CLAIM', () => { taskDetails.status = TASK_CREATED_STATE; taskDetails.permissions = [TASK_CLAIM_PERMISSION]; fixture.detectChanges(); + const canClaimTask = component.canClaimTask(); - const claimBtn = debugElement.query(By.css('[adf-cloud-claim-task]')); - expect(claimBtn.nativeElement).toBeDefined(); - expect(claimBtn.nativeElement.innerText.trim()).toEqual('ADF_CLOUD_TASK_FORM.EMPTY_FORM.BUTTONS.CLAIM'); + expect(canClaimTask).toBe(true); }); it('should not show claim button when status is not CREATED', () => { taskDetails.status = undefined; fixture.detectChanges(); + const canClaimTask = component.canClaimTask(); - const claimBtn = debugElement.query(By.css('[adf-cloud-claim-task]')); - expect(claimBtn).toBeNull(); + expect(canClaimTask).toBe(false); }); it('should not show claim button when status is CREATED and permission not includes CLAIM', () => { taskDetails.status = TASK_CREATED_STATE; taskDetails.permissions = [TASK_VIEW_PERMISSION]; fixture.detectChanges(); + const canClaimTask = component.canClaimTask(); - const claimBtn = debugElement.query(By.css('[adf-cloud-claim-task]')); - expect(claimBtn).toBeNull(); + expect(canClaimTask).toBe(false); }); }); - describe('Cancel button', () => { - it('should show cancel button by default', () => { - component.appName = 'app1'; - component.taskId = 'task1'; - - fixture.detectChanges(); - - const cancelBtn = debugElement.query(By.css('#adf-cloud-cancel-task')); - expect(cancelBtn.nativeElement).toBeDefined(); - expect(cancelBtn.nativeElement.innerText.trim()).toEqual('ADF_CLOUD_TASK_FORM.EMPTY_FORM.BUTTONS.CANCEL'); - }); - - it('should not show cancel button when showCancelButton=false', () => { - component.appName = 'app1'; - component.taskId = 'task1'; - component.showCancelButton = false; - - fixture.detectChanges(); - - const cancelBtn = debugElement.query(By.css('#adf-cloud-cancel-task')); - expect(cancelBtn).toBeNull(); + describe('Inputs', () => { + beforeEach(() => { + fixture.componentRef.setInput('taskDetails', taskDetails); }); - }); - describe('Inputs', () => { it('should not show complete/claim/unclaim buttons when readOnly=true', () => { component.appName = 'app1'; component.taskId = 'task1'; component.readOnly = true; - fixture.detectChanges(); - const completeBtn = debugElement.query(By.css('[adf-cloud-complete-task]')); - expect(completeBtn).toBeNull(); - - const claimBtn = debugElement.query(By.css('[adf-cloud-claim-task]')); - expect(claimBtn).toBeNull(); + const canShowCompleteBtn = component.canCompleteTask(); + expect(canShowCompleteBtn).toBe(false); - const unclaimBtn = debugElement.query(By.css('[adf-cloud-unclaim-task]')); - expect(unclaimBtn).toBeNull(); + const canClaimTask = component.canClaimTask(); + expect(canClaimTask).toBe(false); - const cancelBtn = debugElement.query(By.css('#adf-cloud-cancel-task')); - expect(cancelBtn.nativeElement).toBeDefined(); - expect(cancelBtn.nativeElement.innerText.trim()).toEqual('ADF_CLOUD_TASK_FORM.EMPTY_FORM.BUTTONS.CANCEL'); - }); - - it('should load data when appName changes', () => { - component.taskId = 'task1'; - component.ngOnChanges({ appName: new SimpleChange(null, 'app1', false) }); - expect(getTaskSpy).toHaveBeenCalled(); - }); - - it('should load data when taskId changes', () => { - component.appName = 'app1'; - component.ngOnChanges({ taskId: new SimpleChange(null, 'task1', false) }); - expect(getTaskSpy).toHaveBeenCalled(); - }); - - it('should not load data when appName changes and taskId is not defined', () => { - component.ngOnChanges({ appName: new SimpleChange(null, 'app1', false) }); - expect(getTaskSpy).not.toHaveBeenCalled(); - }); - - it('should not load data when taskId changes and appName is not defined', () => { - component.ngOnChanges({ taskId: new SimpleChange(null, 'task1', false) }); - expect(getTaskSpy).not.toHaveBeenCalled(); + const canUnclaimTask = component.canUnclaimTask(); + expect(canUnclaimTask).toBe(false); }); it('should append additional field validators to the default ones when provided', () => { const mockFirstCustomFieldValidator = new MockFormFieldValidator(); const mockSecondCustomFieldValidator = new MockFormFieldValidator(); - - component.fieldValidators = [mockFirstCustomFieldValidator, mockSecondCustomFieldValidator]; + fixture.componentRef.setInput('fieldValidators', [mockFirstCustomFieldValidator, mockSecondCustomFieldValidator]); fixture.detectChanges(); expect(component.fieldValidators).toEqual([...FORM_FIELD_VALIDATORS, mockFirstCustomFieldValidator, mockSecondCustomFieldValidator]); @@ -301,6 +206,7 @@ describe('TaskFormCloudComponent', () => { describe('Events', () => { beforeEach(() => { + fixture.componentRef.setInput('taskDetails', taskDetails); component.appName = 'app1'; component.taskId = 'task1'; fixture.detectChanges(); @@ -308,53 +214,26 @@ describe('TaskFormCloudComponent', () => { it('should emit cancelClick when cancel button is clicked', async () => { spyOn(component.cancelClick, 'emit').and.stub(); - - fixture.detectChanges(); - - const cancelBtn = debugElement.query(By.css('#adf-cloud-cancel-task')); - cancelBtn.triggerEventHandler('click', {}); + component.onCancelClick(); fixture.detectChanges(); - await fixture.whenStable(); expect(component.cancelClick.emit).toHaveBeenCalledOnceWith('task1'); }); - it('should emit taskCompleted when task is completed', async () => { - spyOn(taskCloudService, 'completeTask').and.returnValue(of({})); - spyOn(component.taskCompleted, 'emit').and.stub(); - - component.ngOnChanges({ appName: new SimpleChange(null, 'app1', false) }); - fixture.detectChanges(); - - const completeBtn = debugElement.query(By.css('[adf-cloud-complete-task]')); - completeBtn.triggerEventHandler('click', {}); - fixture.detectChanges(); - await fixture.whenStable(); - - expect(component.taskCompleted.emit).toHaveBeenCalledOnceWith('task1'); - }); - it('should emit taskClaimed when task is claimed', async () => { spyOn(taskCloudService, 'claimTask').and.returnValue(of({})); spyOn(component, 'hasCandidateUsers').and.returnValue(true); spyOn(component.taskClaimed, 'emit').and.stub(); taskDetails.status = TASK_CREATED_STATE; taskDetails.permissions = [TASK_CLAIM_PERMISSION]; - getTaskSpy.and.returnValue(of(taskDetails)); - - component.ngOnChanges({ appName: new SimpleChange(null, 'app1', false) }); + component.onClaimTask(); fixture.detectChanges(); - const claimBtn = debugElement.query(By.css('[adf-cloud-claim-task]')); - claimBtn.triggerEventHandler('click', {}); - fixture.detectChanges(); - await fixture.whenStable(); expect(component.taskClaimed.emit).toHaveBeenCalledOnceWith('task1'); }); it('should emit error when error occurs', async () => { spyOn(component.error, 'emit').and.stub(); - component.onError({}); fixture.detectChanges(); await fixture.whenStable(); @@ -362,88 +241,15 @@ describe('TaskFormCloudComponent', () => { expect(component.error.emit).toHaveBeenCalled(); }); - it('should reload when task is completed', async () => { - spyOn(taskCloudService, 'completeTask').and.returnValue(of({})); - const reloadSpy = spyOn(component, 'ngOnChanges').and.callThrough(); - - component.ngOnChanges({ appName: new SimpleChange(null, 'app1', false) }); - fixture.detectChanges(); - const completeBtn = debugElement.query(By.css('[adf-cloud-complete-task]')); - - completeBtn.nativeElement.click(); - await fixture.whenStable(); - expect(reloadSpy).toHaveBeenCalled(); - }); - - it('should reload when task is claimed', async () => { - spyOn(taskCloudService, 'claimTask').and.returnValue(of({})); - spyOn(component, 'hasCandidateUsers').and.returnValue(true); - const reloadSpy = spyOn(component, 'ngOnChanges').and.callThrough(); - taskDetails.permissions = [TASK_CLAIM_PERMISSION]; - taskDetails.status = TASK_CREATED_STATE; - getTaskSpy.and.returnValue(of(taskDetails)); - - component.ngOnChanges({ appName: new SimpleChange(null, 'app1', false) }); - fixture.detectChanges(); - const claimBtn = debugElement.query(By.css('[adf-cloud-claim-task]')); - - claimBtn.nativeElement.click(); - await fixture.whenStable(); - expect(reloadSpy).toHaveBeenCalled(); - }); - - it('should emit taskUnclaimed when task is unclaimed', async () => { - spyOn(taskCloudService, 'unclaimTask').and.returnValue(of({})); - const reloadSpy = spyOn(component, 'ngOnChanges').and.callThrough(); - spyOn(component, 'hasCandidateUsers').and.returnValue(true); - - taskDetails.status = TASK_ASSIGNED_STATE; - taskDetails.permissions = [TASK_RELEASE_PERMISSION]; - getTaskSpy.and.returnValue(of(taskDetails)); - - component.ngOnChanges({ appName: new SimpleChange(null, 'app1', false) }); - fixture.detectChanges(); - const unclaimBtn = debugElement.query(By.css('[adf-cloud-unclaim-task]')); - - unclaimBtn.nativeElement.click(); - await fixture.whenStable(); - expect(reloadSpy).toHaveBeenCalled(); - }); - - it('should show loading template while task data is being loaded', async () => { - component.loading = true; - fixture.detectChanges(); - - expect(await loader.hasHarness(MatProgressSpinnerHarness)).toBe(true); - }); - - it('should not show loading template while task data is not being loaded', async () => { - component.loading = false; - fixture.detectChanges(); - - expect(await loader.hasHarness(MatProgressSpinnerHarness)).toBe(false); - }); - it('should emit an executeOutcome event when form outcome executed', () => { const executeOutcomeSpy: jasmine.Spy = spyOn(component.executeOutcome, 'emit'); - component.onFormExecuteOutcome(new FormOutcomeEvent(new FormOutcomeModel(new FormModel()))); expect(executeOutcomeSpy).toHaveBeenCalled(); }); - it('should emit onTaskLoaded on initial load of component', () => { - component.appName = ''; - spyOn(component.onTaskLoaded, 'emit'); - - component.ngOnInit(); - fixture.detectChanges(); - expect(component.onTaskLoaded.emit).toHaveBeenCalledWith(taskDetails); - }); - it('should emit displayModeOn when display mode is turned on', async () => { spyOn(component.displayModeOn, 'emit').and.stub(); - component.onDisplayModeOn(DisplayModeService.DEFAULT_DISPLAY_MODE_CONFIGURATIONS[0]); fixture.detectChanges(); await fixture.whenStable(); @@ -453,7 +259,6 @@ describe('TaskFormCloudComponent', () => { it('should emit displayModeOff when display mode is turned on', async () => { spyOn(component.displayModeOff, 'emit').and.stub(); - component.onDisplayModeOff(DisplayModeService.DEFAULT_DISPLAY_MODE_CONFIGURATIONS[0]); fixture.detectChanges(); await fixture.whenStable(); @@ -462,45 +267,14 @@ describe('TaskFormCloudComponent', () => { }); }); - it('should display task name as title on no form template if showTitle is true', () => { - component.taskId = taskDetails.id; - - fixture.detectChanges(); - const noFormTemplateTitle = debugElement.query(By.css('.adf-form-title')); - - expect(noFormTemplateTitle.nativeElement.innerText).toEqual('Task1'); - }); - - it('should display default name as title on no form template if the task name empty/undefined', () => { - const mockTaskDetailsWithOutName = { id: 'mock-task-id', name: null, formKey: null }; - getTaskSpy.and.returnValue(of(mockTaskDetailsWithOutName)); - component.taskId = 'mock-task-id'; - - fixture.detectChanges(); - const noFormTemplateTitle = debugElement.query(By.css('.adf-form-title')); - - expect(noFormTemplateTitle.nativeElement.innerText).toEqual('FORM.FORM_RENDERER.NAMELESS_TASK'); - }); - - it('should not display no form title if showTitle is set to false', () => { - component.taskId = taskDetails.id; - component.showTitle = false; - - fixture.detectChanges(); - const noFormTemplateTitle = debugElement.query(By.css('.adf-form-title')); - - expect(noFormTemplateTitle).toBeNull(); - }); - it('should call children cloud task form change display mode when changing the display mode', () => { const displayMode = 'displayMode'; component.taskDetails = { ...taskDetails, formKey: 'some-form' }; - fixture.detectChanges(); expect(component.adfCloudForm).toBeDefined(); - const switchToDisplayModeSpy = spyOn(component.adfCloudForm, 'switchToDisplayMode'); + const switchToDisplayModeSpy = spyOn(component.adfCloudForm, 'switchToDisplayMode'); component.switchToDisplayMode(displayMode); expect(switchToDisplayModeSpy).toHaveBeenCalledOnceWith(displayMode); diff --git a/lib/process-services-cloud/src/lib/task/task-form/components/task-form-cloud/task-form-cloud.component.ts b/lib/process-services-cloud/src/lib/task/task-form/components/task-form-cloud/task-form-cloud.component.ts index 8c4c3bb7008..5427b01a244 100644 --- a/lib/process-services-cloud/src/lib/task/task-form/components/task-form-cloud/task-form-cloud.component.ts +++ b/lib/process-services-cloud/src/lib/task/task-form/components/task-form-cloud/task-form-cloud.component.ts @@ -15,15 +15,15 @@ * limitations under the License. */ -import { Component, EventEmitter, Input, OnInit, Output, ViewChild, ViewEncapsulation } from '@angular/core'; -import { TaskDetailsCloudModel } from '../../../start-task/models/task-details-cloud.model'; -import { TaskCloudService } from '../../../services/task-cloud.service'; import { ContentLinkModel, FORM_FIELD_VALIDATORS, FormFieldValidator, FormModel, FormOutcomeEvent, FormRenderingService } from '@alfresco/adf-core'; +import { Component, EventEmitter, Input, OnInit, Output, ViewChild, ViewEncapsulation } from '@angular/core'; +import { FormCloudComponent } from '../../../../form/components/form-cloud.component'; import { AttachFileCloudWidgetComponent } from '../../../../form/components/widgets/attach-file/attach-file-cloud-widget.component'; -import { DropdownCloudWidgetComponent } from '../../../../form/components/widgets/dropdown/dropdown-cloud.widget'; import { DateCloudWidgetComponent } from '../../../../form/components/widgets/date/date-cloud.widget'; +import { DropdownCloudWidgetComponent } from '../../../../form/components/widgets/dropdown/dropdown-cloud.widget'; import { FormCloudDisplayModeConfiguration } from '../../../../services/form-fields.interfaces'; -import { FormCloudComponent } from '../../../../form/components/form-cloud.component'; +import { TaskCloudService } from '../../../services/task-cloud.service'; +import { TaskDetailsCloudModel } from '../../../start-task/models/task-details-cloud.model'; @Component({ selector: 'adf-cloud-task-form', @@ -36,9 +36,11 @@ export class TaskFormCloudComponent implements OnInit { @Input() appName: string = ''; - /**Candidates user and groups */ + /**Candidates users*/ @Input() candidateUsers: string[] = []; + + /**Candidates groups */ @Input() candidateGroups: string[] = []; @@ -162,6 +164,10 @@ export class TaskFormCloudComponent implements OnInit { return !this.readOnly && this.taskCloudService.canClaimTask(this.taskDetails) && this.hasCandidateUsersOrGroups(); } + canUnclaimTask(): boolean { + return !this.readOnly && this.taskCloudService.canUnclaimTask(this.taskDetails) && this.hasCandidateUsersOrGroups(); + } + hasCandidateUsers(): boolean { return this.candidateUsers.length !== 0; } @@ -174,10 +180,6 @@ export class TaskFormCloudComponent implements OnInit { return this.hasCandidateUsers() || this.hasCandidateGroups(); } - canUnclaimTask(): boolean { - return !this.readOnly && this.taskCloudService.canUnclaimTask(this.taskDetails) && this.hasCandidateUsersOrGroups(); - } - isReadOnly(): boolean { return this.readOnly || !this.taskCloudService.canCompleteTask(this.taskDetails); } diff --git a/lib/process-services-cloud/src/lib/task/task-form/components/user-task-cloud-buttons/user-task-cloud-buttons.component.html b/lib/process-services-cloud/src/lib/task/task-form/components/user-task-cloud-buttons/user-task-cloud-buttons.component.html index f5821a8a76d..73144426e01 100644 --- a/lib/process-services-cloud/src/lib/task/task-form/components/user-task-cloud-buttons/user-task-cloud-buttons.component.html +++ b/lib/process-services-cloud/src/lib/task/task-form/components/user-task-cloud-buttons/user-task-cloud-buttons.component.html @@ -6,6 +6,7 @@ {{'ADF_CLOUD_TASK_FORM.EMPTY_FORM.BUTTONS.CANCEL' | translate}} diff --git a/lib/process-services-cloud/src/lib/task/task-form/components/user-task-cloud/user-task-cloud.component.scss b/lib/process-services-cloud/src/lib/task/task-form/components/user-task-cloud/user-task-cloud.component.scss index a30d10d50ed..0878f3b860c 100644 --- a/lib/process-services-cloud/src/lib/task/task-form/components/user-task-cloud/user-task-cloud.component.scss +++ b/lib/process-services-cloud/src/lib/task/task-form/components/user-task-cloud/user-task-cloud.component.scss @@ -1,6 +1,9 @@ -.adf-task-form-cloud-container { - width: 100%; +.adf-user-task-cloud-container { height: 100%; + + > div { + height: 100%; + } } .adf-user-task-cloud-spinner { diff --git a/lib/process-services-cloud/src/lib/task/task-form/components/user-task-cloud/user-task-cloud.component.spec.ts b/lib/process-services-cloud/src/lib/task/task-form/components/user-task-cloud/user-task-cloud.component.spec.ts index 783ea62a73e..cbdd777850f 100644 --- a/lib/process-services-cloud/src/lib/task/task-form/components/user-task-cloud/user-task-cloud.component.spec.ts +++ b/lib/process-services-cloud/src/lib/task/task-form/components/user-task-cloud/user-task-cloud.component.spec.ts @@ -15,23 +15,457 @@ * limitations under the License. */ +import { NoopTranslateModule } from '@alfresco/adf-core'; +import { + TASK_ASSIGNED_STATE, + TASK_CLAIM_PERMISSION, + TASK_CREATED_STATE, + TASK_RELEASE_PERMISSION, + TASK_VIEW_PERMISSION, + TaskCloudService, + TaskDetailsCloudModel, + TaskFormCloudComponent +} from '@alfresco/adf-process-services-cloud'; +import { HarnessLoader } from '@angular/cdk/testing'; +import { TestbedHarnessEnvironment } from '@angular/cdk/testing/testbed'; +import { SimpleChange } from '@angular/core'; import { ComponentFixture, TestBed } from '@angular/core/testing'; +import { MatButtonHarness } from '@angular/material/button/testing'; +import { MatCardHarness } from '@angular/material/card/testing'; +import { MatProgressSpinnerHarness } from '@angular/material/progress-spinner/testing'; +import { ProcessServiceCloudTestingModule } from 'lib/process-services-cloud/src/lib/testing/process-service-cloud.testing.module'; +import { of } from 'rxjs'; +import { IdentityUserService } from '../../../../people/services/identity-user.service'; import { UserTaskCloudComponent } from './user-task-cloud.component'; +const taskDetails: TaskDetailsCloudModel = { + appName: 'simple-app', + assignee: 'admin.adf', + completedDate: null, + createdDate: new Date(1555419255340), + description: null, + formKey: null, + id: 'bd6b1741-6046-11e9-80f0-0a586460040d', + name: 'Task1', + owner: 'admin.adf', + standalone: false, + status: TASK_ASSIGNED_STATE, + permissions: [TASK_VIEW_PERMISSION] +}; + describe('UserTaskCloudComponent', () => { let component: UserTaskCloudComponent; let fixture: ComponentFixture; + let taskCloudService: TaskCloudService; + let getTaskSpy: jasmine.Spy; + let getCurrentUserSpy: jasmine.Spy; + let loader: HarnessLoader; + let identityUserService: IdentityUserService; beforeEach(() => { TestBed.configureTestingModule({ - imports: [UserTaskCloudComponent] + imports: [NoopTranslateModule, ProcessServiceCloudTestingModule], + declarations: [UserTaskCloudComponent, TaskFormCloudComponent] }); fixture = TestBed.createComponent(UserTaskCloudComponent); component = fixture.componentInstance; + loader = TestbedHarnessEnvironment.loader(fixture); + taskCloudService = TestBed.inject(TaskCloudService); + identityUserService = TestBed.inject(IdentityUserService); + + getTaskSpy = spyOn(taskCloudService, 'getTaskById').and.returnValue(of(taskDetails)); + getCurrentUserSpy = spyOn(identityUserService, 'getCurrentUserInfo').and.returnValue({ username: 'admin.adf' }); + spyOn(taskCloudService, 'getCandidateGroups').and.returnValue(of([])); + spyOn(taskCloudService, 'getCandidateUsers').and.returnValue(of([])); fixture.detectChanges(); }); - it('should create', () => { - expect(component).toBeTruthy(); + describe('Complete button', () => { + beforeEach(() => { + fixture.componentRef.setInput('showCompleteButton', true); + fixture.componentRef.setInput('appName', 'app1'); + fixture.componentRef.setInput('taskId', 'task1'); + getTaskSpy.and.returnValue(of({ ...taskDetails })); + fixture.detectChanges(); + fixture.whenStable(); + }); + + it('should show complete button when status is ASSIGNED', async () => { + const completeButton = await loader.getHarnessOrNull(MatButtonHarness.with({ selector: '#adf-form-complete' })); + + expect(completeButton).not.toBeNull(); + }); + + it('should not show complete button when status is ASSIGNED but assigned to a different person', async () => { + getCurrentUserSpy.and.returnValue({}); + fixture.detectChanges(); + const completeButton = await loader.getHarnessOrNull(MatButtonHarness.with({ selector: '#adf-form-complete' })); + + expect(completeButton).toBeNull(); + }); + + it('should not show complete button when showCompleteButton=false', async () => { + fixture.componentRef.setInput('showCompleteButton', false); + fixture.detectChanges(); + const completeButton = await loader.getHarnessOrNull(MatButtonHarness.with({ selector: '#adf-form-complete' })); + + expect(completeButton).toBeNull(); + }); + }); + + describe('Claim/Unclaim buttons', () => { + beforeEach(() => { + spyOn(component, 'hasCandidateUsers').and.returnValue(true); + component.taskDetails = taskDetails; + fixture.componentRef.setInput('appName', 'app1'); + fixture.componentRef.setInput('taskId', 'task1'); + getTaskSpy.and.returnValue(of(taskDetails)); + fixture.detectChanges(); + }); + + it('should not show release button for standalone task', async () => { + component.taskDetails.permissions = [TASK_RELEASE_PERMISSION]; + component.taskDetails.standalone = true; + fixture.detectChanges(); + const unclaimBtn = await loader.getHarnessOrNull(MatButtonHarness.with({ selector: '[adf-cloud-unclaim-task]' })); + + expect(unclaimBtn).toBeNull(); + }); + + it('should not show claim button for standalone task', async () => { + component.taskDetails.status = TASK_CREATED_STATE; + component.taskDetails.permissions = [TASK_CLAIM_PERMISSION]; + component.taskDetails.standalone = true; + fixture.detectChanges(); + const claimBtn = await loader.getHarnessOrNull(MatButtonHarness.with({ selector: '[adf-cloud-claim-task]' })); + + expect(claimBtn).toBeNull(); + }); + + it('should show release button when task is assigned to one of the candidate users', async () => { + component.taskDetails = { ...taskDetails, standalone: false, status: TASK_ASSIGNED_STATE, permissions: [TASK_RELEASE_PERMISSION] }; + fixture.detectChanges(); + const unclaimBtn = await loader.getHarnessOrNull(MatButtonHarness.with({ selector: '[adf-cloud-unclaim-task]' })); + expect(unclaimBtn).not.toBeNull(); + + const unclaimBtnLabel = await unclaimBtn.getText(); + expect(unclaimBtnLabel).toEqual('ADF_CLOUD_TASK_FORM.EMPTY_FORM.BUTTONS.UNCLAIM'); + }); + + it('should not show unclaim button when status is ASSIGNED but assigned to different person', async () => { + getCurrentUserSpy.and.returnValue({}); + fixture.detectChanges(); + const unclaimBtn = await loader.getHarnessOrNull(MatButtonHarness.with({ selector: '[adf-cloud-unclaim-task]' })); + + expect(unclaimBtn).toBeNull(); + }); + + it('should not show unclaim button when status is not ASSIGNED', async () => { + component.taskDetails.status = undefined; + fixture.detectChanges(); + const unclaimBtn = await loader.getHarnessOrNull(MatButtonHarness.with({ selector: '[adf-cloud-unclaim-task]' })); + + expect(unclaimBtn).toBeNull(); + }); + + it('should not show unclaim button when status is ASSIGNED and permissions not include RELEASE', async () => { + component.taskDetails.status = TASK_ASSIGNED_STATE; + component.taskDetails.permissions = [TASK_VIEW_PERMISSION]; + fixture.detectChanges(); + const unclaimBtn = await loader.getHarnessOrNull(MatButtonHarness.with({ selector: '[adf-cloud-unclaim-task]' })); + + expect(unclaimBtn).toBeNull(); + }); + + it('should show claim button when status is CREATED and permission includes CLAIM', async () => { + component.taskDetails.standalone = false; + component.taskDetails.status = TASK_CREATED_STATE; + component.taskDetails.permissions = [TASK_CLAIM_PERMISSION]; + fixture.detectChanges(); + + const claimBtn = await loader.getHarnessOrNull(MatButtonHarness.with({ selector: '[adf-cloud-claim-task]' })); + expect(claimBtn).not.toBeNull(); + + const claimBtnLabel = await claimBtn.getText(); + expect(claimBtnLabel).toEqual('ADF_CLOUD_TASK_FORM.EMPTY_FORM.BUTTONS.CLAIM'); + }); + + it('should not show claim button when status is not CREATED', async () => { + component.taskDetails.status = undefined; + fixture.detectChanges(); + const claimBtn = await loader.getHarnessOrNull(MatButtonHarness.with({ selector: '[adf-cloud-claim-task]' })); + + expect(claimBtn).toBeNull(); + }); + + it('should not show claim button when status is CREATED and permission not includes CLAIM', async () => { + component.taskDetails.status = TASK_CREATED_STATE; + component.taskDetails.permissions = [TASK_VIEW_PERMISSION]; + fixture.detectChanges(); + const claimBtn = await loader.getHarnessOrNull(MatButtonHarness.with({ selector: '[adf-cloud-claim-task]' })); + + expect(claimBtn).toBeNull(); + }); + }); + + describe('Cancel button', () => { + beforeEach(() => { + fixture.componentRef.setInput('appName', 'app1'); + fixture.componentRef.setInput('taskId', 'task1'); + fixture.detectChanges(); + }); + + it('should show cancel button by default', async () => { + const cancelBtn = await loader.getHarnessOrNull(MatButtonHarness.with({ selector: '#adf-cloud-cancel-task' })); + expect(cancelBtn).toBeDefined(); + + const cancelBtnLabel = await cancelBtn.getText(); + expect(cancelBtnLabel).toEqual('ADF_CLOUD_TASK_FORM.EMPTY_FORM.BUTTONS.CANCEL'); + }); + + it('should not show cancel button when showCancelButton=false', async () => { + fixture.componentRef.setInput('showCancelButton', false); + fixture.detectChanges(); + const cancelBtn = await loader.getHarnessOrNull(MatButtonHarness.with({ selector: '#adf-cloud-cancel-task' })); + + expect(cancelBtn).toBeNull(); + }); + }); + + describe('Inputs', () => { + it('should not show complete/claim/unclaim buttons when readOnly=true', async () => { + getTaskSpy.and.returnValue(of(taskDetails)); + fixture.componentRef.setInput('appName', 'app1'); + fixture.componentRef.setInput('taskId', 'task1'); + fixture.componentRef.setInput('readOnly', true); + fixture.componentRef.setInput('showCancelButton', true); + component.getTaskType(); + fixture.detectChanges(); + await fixture.whenStable(); + + const completeBtn = await loader.getHarnessOrNull(MatButtonHarness.with({ selector: '[adf-cloud-complete-task]' })); + expect(completeBtn).toBeNull(); + + const claimBtn = await loader.getHarnessOrNull(MatButtonHarness.with({ selector: '[adf-cloud-claim-task]' })); + expect(claimBtn).toBeNull(); + + const unclaimBtn = await loader.getHarnessOrNull(MatButtonHarness.with({ selector: '[adf-cloud-unclaim-task]' })); + expect(unclaimBtn).toBeNull(); + + const cancelBtn = await loader.getHarnessOrNull(MatButtonHarness.with({ selector: '#adf-cloud-cancel-task' })); + expect(cancelBtn).toBeDefined(); + + const cancelBtnLabel = await cancelBtn.getText(); + expect(cancelBtnLabel).toEqual('ADF_CLOUD_TASK_FORM.EMPTY_FORM.BUTTONS.CANCEL'); + }); + + it('should load data when appName changes', () => { + component.taskId = 'task1'; + component.ngOnChanges({ appName: new SimpleChange(null, 'app1', false) }); + + expect(getTaskSpy).toHaveBeenCalled(); + }); + + it('should load data when taskId changes', () => { + component.appName = 'app1'; + component.ngOnChanges({ taskId: new SimpleChange(null, 'task1', false) }); + + expect(getTaskSpy).toHaveBeenCalled(); + }); + + it('should not load data when appName changes and taskId is not defined', async () => { + fixture.componentRef.setInput('taskId', null); + fixture.detectChanges(); + + expect(component.taskId).toBeNull(); + + component.ngOnChanges({ appName: new SimpleChange(null, 'app1', false) }); + await fixture.whenStable(); + + expect(getTaskSpy).not.toHaveBeenCalled(); + }); + + it('should not load data when taskId changes and appName is not defined', async () => { + component.ngOnChanges({ taskId: new SimpleChange(null, 'task1', false) }); + + expect(getTaskSpy).not.toHaveBeenCalled(); + }); + }); + + describe('Events', () => { + beforeEach(() => { + fixture.componentRef.setInput('appName', 'app1'); + fixture.componentRef.setInput('taskId', 'task1'); + fixture.componentRef.setInput('showCancelButton', true); + fixture.detectChanges(); + }); + + it('should emit cancelClick when cancel button is clicked', async () => { + spyOn(component.cancelClick, 'emit').and.stub(); + fixture.detectChanges(); + + const cancelBtn = await loader.getHarnessOrNull(MatButtonHarness.with({ selector: '#adf-cloud-cancel-task' })); + await cancelBtn.click(); + fixture.detectChanges(); + await fixture.whenStable(); + + expect(component.cancelClick.emit).toHaveBeenCalledOnceWith('task1'); + }); + + it('should emit taskCompleted when task is completed', async () => { + component.taskDetails.status = TASK_ASSIGNED_STATE; + spyOn(taskCloudService, 'completeTask').and.returnValue(of({})); + spyOn(component.taskCompleted, 'emit').and.stub(); + component.ngOnChanges({ appName: new SimpleChange(null, 'app1', false) }); + fixture.detectChanges(); + const completeBtn = await loader.getHarnessOrNull(MatButtonHarness.with({ selector: '[adf-cloud-complete-task]' })); + await completeBtn.click(); + fixture.detectChanges(); + await fixture.whenStable(); + + expect(component.taskCompleted.emit).toHaveBeenCalledOnceWith('task1'); + }); + + it('should emit taskClaimed when task is claimed', async () => { + spyOn(taskCloudService, 'claimTask').and.returnValue(of({})); + spyOn(component, 'hasCandidateUsers').and.returnValue(true); + spyOn(component.taskClaimed, 'emit').and.stub(); + taskDetails.status = TASK_CREATED_STATE; + taskDetails.permissions = [TASK_CLAIM_PERMISSION]; + getTaskSpy.and.returnValue(of(taskDetails)); + + component.ngOnChanges({ appName: new SimpleChange(null, 'app1', false) }); + fixture.detectChanges(); + + const claimBtn = await loader.getHarnessOrNull(MatButtonHarness.with({ selector: '[adf-cloud-claim-task]' })); + await claimBtn.click(); + fixture.detectChanges(); + await fixture.whenStable(); + + expect(component.taskClaimed.emit).toHaveBeenCalledOnceWith('task1'); + }); + + it('should emit error when error occurs', async () => { + spyOn(component.error, 'emit').and.stub(); + component.onError({}); + fixture.detectChanges(); + await fixture.whenStable(); + + expect(component.error.emit).toHaveBeenCalled(); + }); + + it('should reload when task is completed', async () => { + spyOn(taskCloudService, 'completeTask').and.returnValue(of({})); + const reloadSpy = spyOn(component, 'ngOnChanges').and.callThrough(); + component.taskDetails.status = TASK_ASSIGNED_STATE; + + component.ngOnChanges({ appName: new SimpleChange(null, 'app1', false) }); + fixture.detectChanges(); + await fixture.whenStable(); + + const completeBtn = await loader.getHarnessOrNull(MatButtonHarness.with({ selector: '[adf-cloud-complete-task]' })); + await completeBtn.click(); + await fixture.whenStable(); + + expect(reloadSpy).toHaveBeenCalled(); + }); + + it('should reload when task is claimed', async () => { + spyOn(taskCloudService, 'claimTask').and.returnValue(of({})); + spyOn(component, 'hasCandidateUsers').and.returnValue(true); + const reloadSpy = spyOn(component, 'ngOnChanges').and.callThrough(); + taskDetails.permissions = [TASK_CLAIM_PERMISSION]; + taskDetails.status = TASK_CREATED_STATE; + getTaskSpy.and.returnValue(of(taskDetails)); + + component.ngOnChanges({ appName: new SimpleChange(null, 'app1', false) }); + fixture.detectChanges(); + + const claimBtn = await loader.getHarnessOrNull(MatButtonHarness.with({ selector: '[adf-cloud-claim-task]' })); + await claimBtn.click(); + await fixture.whenStable(); + + expect(reloadSpy).toHaveBeenCalled(); + }); + + it('should emit taskUnclaimed when task is unclaimed', async () => { + spyOn(taskCloudService, 'unclaimTask').and.returnValue(of({})); + const reloadSpy = spyOn(component, 'ngOnChanges').and.callThrough(); + spyOn(component, 'hasCandidateUsers').and.returnValue(true); + + taskDetails.status = TASK_ASSIGNED_STATE; + taskDetails.permissions = [TASK_RELEASE_PERMISSION]; + getTaskSpy.and.returnValue(of(taskDetails)); + + component.ngOnChanges({ appName: new SimpleChange(null, 'app1', false) }); + fixture.detectChanges(); + const unclaimBtn = await loader.getHarnessOrNull(MatButtonHarness.with({ selector: '[adf-cloud-unclaim-task]' })); + await unclaimBtn.click(); + await fixture.whenStable(); + + expect(reloadSpy).toHaveBeenCalled(); + }); + + it('should show loading template while task data is being loaded', async () => { + component.loading = true; + fixture.detectChanges(); + + expect(await loader.hasHarness(MatProgressSpinnerHarness)).toBe(true); + }); + + it('should not show loading template while task data is not being loaded', async () => { + component.loading = false; + fixture.detectChanges(); + + expect(await loader.hasHarness(MatProgressSpinnerHarness)).toBe(false); + }); + + it('should emit onTaskLoaded on initial load of component', () => { + component.appName = ''; + spyOn(component.onTaskLoaded, 'emit'); + + component.ngOnInit(); + fixture.detectChanges(); + expect(component.onTaskLoaded.emit).toHaveBeenCalledWith(taskDetails); + }); + }); + + it('should display task name as title on no form template if showTitle is true', async () => { + fixture.componentRef.setInput('appName', 'app1'); + fixture.componentRef.setInput('taskId', 'task1'); + component.taskDetails = { ...taskDetails }; + fixture.detectChanges(); + + const noFormTemplateTitle = await loader.getHarnessOrNull(MatCardHarness); + const noFormTemplateTitleText = await noFormTemplateTitle.getTitleText(); + + expect(noFormTemplateTitleText).toEqual('Task1'); + }); + + it('should display default name as title on no form template if the task name empty/undefined', async () => { + fixture.componentRef.setInput('appName', 'app1'); + fixture.componentRef.setInput('taskId', 'mock-task-id'); + const mockTaskDetailsWithOutName = { id: 'mock-task-id', name: null, formKey: null }; + getTaskSpy.and.returnValue(of(mockTaskDetailsWithOutName)); + + fixture.detectChanges(); + const matCard = await loader.getHarnessOrNull(MatCardHarness); + const noFormTemplateTitle = await matCard.getTitleText(); + + expect(noFormTemplateTitle).toEqual('FORM.FORM_RENDERER.NAMELESS_TASK'); + }); + + it('should not display no form title if showTitle is set to false', async () => { + fixture.componentRef.setInput('appName', 'app1'); + fixture.componentRef.setInput('taskId', 'task1'); + fixture.componentRef.setInput('showTitle', false); + component.showTitle = false; + + fixture.detectChanges(); + const matCard = await loader.getHarnessOrNull(MatCardHarness); + expect(matCard).toBeDefined(); + + const noFormTemplateTitleText = await matCard.getTitleText(); + expect(noFormTemplateTitleText).toBe(''); }); }); diff --git a/lib/process-services-cloud/src/lib/task/task-form/components/user-task-cloud/user-task-cloud.component.ts b/lib/process-services-cloud/src/lib/task/task-form/components/user-task-cloud/user-task-cloud.component.ts index fdeb75e3913..37ef552d0f9 100644 --- a/lib/process-services-cloud/src/lib/task/task-form/components/user-task-cloud/user-task-cloud.component.ts +++ b/lib/process-services-cloud/src/lib/task/task-form/components/user-task-cloud/user-task-cloud.component.ts @@ -120,6 +120,7 @@ export class UserTaskCloudComponent implements OnInit, OnChanges { candidateUsers: string[] = []; candidateGroups: string[] = []; loading: boolean = false; + screenId: string; taskDetails: TaskDetailsCloudModel; taskType: TaskTypesType; taskTypeEnum = TaskTypes; @@ -144,6 +145,8 @@ export class UserTaskCloudComponent implements OnInit, OnChanges { this.taskType = this.taskTypeEnum.Form; } else if (this.taskDetails && !!this.taskDetails.formKey && this.taskDetails.formKey.includes(this.taskTypeEnum.Screen)) { this.taskType = this.taskTypeEnum.Screen; + const screenId = this.taskDetails.formKey.replace(this.taskTypeEnum.Screen + '-', ''); + this.screenId = screenId; } else { this.taskType = this.taskTypeEnum.None; } diff --git a/lib/process-services-cloud/src/lib/task/task-form/task-form.module.ts b/lib/process-services-cloud/src/lib/task/task-form/task-form.module.ts index bd3a42e4598..75d5b8ad046 100644 --- a/lib/process-services-cloud/src/lib/task/task-form/task-form.module.ts +++ b/lib/process-services-cloud/src/lib/task/task-form/task-form.module.ts @@ -20,7 +20,6 @@ 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/task-form-cloud.component'; import { CoreModule } from '@alfresco/adf-core'; import { ScreenCloudComponent } from '../../screen/components/screen-cloud/screen-cloud.component'; From b3569c9b0c06d9a0c58a4794e31e69d10fa5db7e Mon Sep 17 00:00:00 2001 From: "tomek.hanaj" Date: Wed, 11 Dec 2024 14:54:14 +0100 Subject: [PATCH 3/8] removed obsolete service, updated interface --- lib/core/src/lib/form/public-api.ts | 1 - .../services/screen-rendering.service.spec.ts | 33 ------------------- .../form/services/screen-rendering.service.ts | 24 -------------- .../user-task-cloud.interface.ts | 3 ++ 4 files changed, 3 insertions(+), 58 deletions(-) delete mode 100644 lib/core/src/lib/form/services/screen-rendering.service.spec.ts delete mode 100644 lib/core/src/lib/form/services/screen-rendering.service.ts diff --git a/lib/core/src/lib/form/public-api.ts b/lib/core/src/lib/form/public-api.ts index e89e9fb0647..ba1d920878c 100644 --- a/lib/core/src/lib/form/public-api.ts +++ b/lib/core/src/lib/form/public-api.ts @@ -28,7 +28,6 @@ export * from './services/form-rendering.service'; export * from './services/form.service'; export * from './services/form-validation-service.interface'; export * from './services/widget-visibility.service'; -export * from './services/screen-rendering.service'; export * from './pipes'; diff --git a/lib/core/src/lib/form/services/screen-rendering.service.spec.ts b/lib/core/src/lib/form/services/screen-rendering.service.spec.ts deleted file mode 100644 index d3a2e89cde8..00000000000 --- a/lib/core/src/lib/form/services/screen-rendering.service.spec.ts +++ /dev/null @@ -1,33 +0,0 @@ -/*! - * @license - * Copyright © 2005-2024 Hyland Software, Inc. and its affiliates. All rights reserved. - * - * 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 { TestBed } from '@angular/core/testing'; - -import { ScreenRenderingService } from './screen-rendering.service'; - -describe('ScreenRenderingService', () => { - let service: ScreenRenderingService; - - beforeEach(() => { - TestBed.configureTestingModule({}); - service = TestBed.inject(ScreenRenderingService); - }); - - it('should be created', () => { - expect(service).toBeTruthy(); - }); -}); diff --git a/lib/core/src/lib/form/services/screen-rendering.service.ts b/lib/core/src/lib/form/services/screen-rendering.service.ts deleted file mode 100644 index 593ac83c80f..00000000000 --- a/lib/core/src/lib/form/services/screen-rendering.service.ts +++ /dev/null @@ -1,24 +0,0 @@ -/*! - * @license - * Copyright © 2005-2024 Hyland Software, Inc. and its affiliates. All rights reserved. - * - * 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 { DynamicComponentMapper } from '../../common/services/dynamic-component-mapper.service'; - -@Injectable({ - providedIn: 'root' -}) -export class ScreenRenderingService extends DynamicComponentMapper {} diff --git a/lib/process-services-cloud/src/lib/task/task-form/components/user-task-cloud/user-task-cloud.interface.ts b/lib/process-services-cloud/src/lib/task/task-form/components/user-task-cloud/user-task-cloud.interface.ts index 773bffde36a..c76cef0dba5 100644 --- a/lib/process-services-cloud/src/lib/task/task-form/components/user-task-cloud/user-task-cloud.interface.ts +++ b/lib/process-services-cloud/src/lib/task/task-form/components/user-task-cloud/user-task-cloud.interface.ts @@ -18,6 +18,9 @@ import { EventEmitter } from '@angular/core'; export interface UserTaskCustomUi { + appName: string; + taskId: string; + screenId: string; error: EventEmitter; cancelClick: EventEmitter; taskClaimed: EventEmitter; From c57237e08ee415f875dba2734f83b3ce1477c3f1 Mon Sep 17 00:00:00 2001 From: "tomek.hanaj" Date: Mon, 16 Dec 2024 11:30:43 +0100 Subject: [PATCH 4/8] passing data to created component, removed obsolete files --- .../components/screen-cloud/screen-cloud.component.scss | 3 --- .../components/screen-cloud/screen-cloud.component.ts | 9 +++++++++ .../user-task-cloud-buttons.component.scss | 3 --- 3 files changed, 9 insertions(+), 6 deletions(-) delete mode 100644 lib/process-services-cloud/src/lib/screen/components/screen-cloud/screen-cloud.component.scss delete mode 100644 lib/process-services-cloud/src/lib/task/task-form/components/user-task-cloud-buttons/user-task-cloud-buttons.component.scss diff --git a/lib/process-services-cloud/src/lib/screen/components/screen-cloud/screen-cloud.component.scss b/lib/process-services-cloud/src/lib/screen/components/screen-cloud/screen-cloud.component.scss deleted file mode 100644 index d16f66ad282..00000000000 --- a/lib/process-services-cloud/src/lib/screen/components/screen-cloud/screen-cloud.component.scss +++ /dev/null @@ -1,3 +0,0 @@ -div { - background-color: inherit; -} diff --git a/lib/process-services-cloud/src/lib/screen/components/screen-cloud/screen-cloud.component.ts b/lib/process-services-cloud/src/lib/screen/components/screen-cloud/screen-cloud.component.ts index 1684fe789ce..968a4bc308c 100644 --- a/lib/process-services-cloud/src/lib/screen/components/screen-cloud/screen-cloud.component.ts +++ b/lib/process-services-cloud/src/lib/screen/components/screen-cloud/screen-cloud.component.ts @@ -51,6 +51,15 @@ export class ScreenCloudComponent implements OnInit { this.screenComponent = { type: this.screenId }; const componentType = this.screenRenderingService.resolveComponentType(this.screenComponent); this.componentRef = this.container.createComponent(componentType); + if (this.taskId) { + this.componentRef.setInput('taskId', this.taskId); + } + if (this.appName) { + this.componentRef.setInput('appName', this.appName); + } + if (this.screenId) { + this.componentRef.setInput('screenId', this.screenId); + } } } } diff --git a/lib/process-services-cloud/src/lib/task/task-form/components/user-task-cloud-buttons/user-task-cloud-buttons.component.scss b/lib/process-services-cloud/src/lib/task/task-form/components/user-task-cloud-buttons/user-task-cloud-buttons.component.scss deleted file mode 100644 index d16f66ad282..00000000000 --- a/lib/process-services-cloud/src/lib/task/task-form/components/user-task-cloud-buttons/user-task-cloud-buttons.component.scss +++ /dev/null @@ -1,3 +0,0 @@ -div { - background-color: inherit; -} From fdf9281eeb7bd14633814b5e8ddec3acbc643945 Mon Sep 17 00:00:00 2001 From: "tomek.hanaj" Date: Tue, 17 Dec 2024 13:38:17 +0100 Subject: [PATCH 5/8] [AAE-29424] applied pr comments --- .../screen-cloud/screen-cloud.component.html | 4 +- .../screen-cloud.component.spec.ts | 10 +-- .../screen-cloud/screen-cloud.component.ts | 11 ++- .../lib/services/screen-rendering.service.ts | 2 +- .../task-form-cloud.component.html | 49 ++++++------- .../user-task-cloud-buttons.component.html | 19 +++--- .../user-task-cloud.component.html | 28 +++++--- .../user-task-cloud.component.ts | 68 +++++++++---------- .../lib/task/task-form/task-form.module.ts | 4 +- 9 files changed, 101 insertions(+), 94 deletions(-) diff --git a/lib/process-services-cloud/src/lib/screen/components/screen-cloud/screen-cloud.component.html b/lib/process-services-cloud/src/lib/screen/components/screen-cloud/screen-cloud.component.html index 3e609e3baaf..94f8ea35f10 100644 --- a/lib/process-services-cloud/src/lib/screen/components/screen-cloud/screen-cloud.component.html +++ b/lib/process-services-cloud/src/lib/screen/components/screen-cloud/screen-cloud.component.html @@ -1,3 +1 @@ -
-
-
+
diff --git a/lib/process-services-cloud/src/lib/screen/components/screen-cloud/screen-cloud.component.spec.ts b/lib/process-services-cloud/src/lib/screen/components/screen-cloud/screen-cloud.component.spec.ts index 5014b4a1ce3..43fc628f48e 100644 --- a/lib/process-services-cloud/src/lib/screen/components/screen-cloud/screen-cloud.component.spec.ts +++ b/lib/process-services-cloud/src/lib/screen/components/screen-cloud/screen-cloud.component.spec.ts @@ -17,7 +17,7 @@ import { ComponentFixture, TestBed } from '@angular/core/testing'; -import { ScreenCloudComponent } from './screen-cloud.component'; +import { TaskScreenCloudComponent } from './screen-cloud.component'; import { Component } from '@angular/core'; import { CommonModule } from '@angular/common'; import { ScreenRenderingService } from '../../../services/public-api'; @@ -31,15 +31,15 @@ import { By } from '@angular/platform-browser'; }) class TestComponent {} -describe('ScreenCloudComponent', () => { - let fixture: ComponentFixture; +describe('TaskScreenCloudComponent', () => { + let fixture: ComponentFixture; let screenRenderingService: ScreenRenderingService; beforeEach(() => { TestBed.configureTestingModule({ - imports: [ScreenCloudComponent, TestComponent] + imports: [TaskScreenCloudComponent, TestComponent] }); - fixture = TestBed.createComponent(ScreenCloudComponent); + fixture = TestBed.createComponent(TaskScreenCloudComponent); screenRenderingService = TestBed.inject(ScreenRenderingService); screenRenderingService.register({ ['test']: () => TestComponent }); fixture.componentRef.setInput('screenId', 'test'); diff --git a/lib/process-services-cloud/src/lib/screen/components/screen-cloud/screen-cloud.component.ts b/lib/process-services-cloud/src/lib/screen/components/screen-cloud/screen-cloud.component.ts index 968a4bc308c..3ea9c1a4bb9 100644 --- a/lib/process-services-cloud/src/lib/screen/components/screen-cloud/screen-cloud.component.ts +++ b/lib/process-services-cloud/src/lib/screen/components/screen-cloud/screen-cloud.component.ts @@ -15,18 +15,17 @@ * limitations under the License. */ -import { DynamicComponentModel } from '@alfresco/adf-core'; -import { ScreenRenderingService } from '../../../services/public-api'; import { CommonModule } from '@angular/common'; import { Component, ComponentRef, inject, Input, OnInit, ViewChild, ViewContainerRef } from '@angular/core'; +import { ScreenRenderingService } from '../../../services/public-api'; @Component({ - selector: 'adf-cloud-screen-cloud', + selector: 'adf-cloud-task-screen', standalone: true, imports: [CommonModule], template: '
' }) -export class ScreenCloudComponent implements OnInit { +export class TaskScreenCloudComponent implements OnInit { /** Task id to fetch corresponding form and values. */ @Input() taskId: string; /** App id to fetch corresponding form and values. */ @@ -41,15 +40,13 @@ export class ScreenCloudComponent implements OnInit { @ViewChild('container', { read: ViewContainerRef, static: true }) container: ViewContainerRef; - screenComponent: DynamicComponentModel; componentRef: ComponentRef; private readonly screenRenderingService = inject(ScreenRenderingService); ngOnInit() { if (this.screenId) { - this.screenComponent = { type: this.screenId }; - const componentType = this.screenRenderingService.resolveComponentType(this.screenComponent); + const componentType = this.screenRenderingService.resolveComponentType({ type: this.screenId }); this.componentRef = this.container.createComponent(componentType); if (this.taskId) { this.componentRef.setInput('taskId', this.taskId); diff --git a/lib/process-services-cloud/src/lib/services/screen-rendering.service.ts b/lib/process-services-cloud/src/lib/services/screen-rendering.service.ts index 7cc13efe0ea..156f727a454 100644 --- a/lib/process-services-cloud/src/lib/services/screen-rendering.service.ts +++ b/lib/process-services-cloud/src/lib/services/screen-rendering.service.ts @@ -15,8 +15,8 @@ * limitations under the License. */ +import { DynamicComponentMapper } from '@alfresco/adf-core'; import { Injectable } from '@angular/core'; -import { DynamicComponentMapper } from '../../../../core/src/lib/common/services/dynamic-component-mapper.service'; @Injectable({ providedIn: 'root' diff --git a/lib/process-services-cloud/src/lib/task/task-form/components/task-form-cloud/task-form-cloud.component.html b/lib/process-services-cloud/src/lib/task/task-form/components/task-form-cloud/task-form-cloud.component.html index 25d27e255f4..0b85839fe4c 100644 --- a/lib/process-services-cloud/src/lib/task/task-form/components/task-form-cloud/task-form-cloud.component.html +++ b/lib/process-services-cloud/src/lib/task/task-form/components/task-form-cloud/task-form-cloud.component.html @@ -1,25 +1,27 @@ -
- +
+ + (error)="onError($event)" + > diff --git a/lib/process-services-cloud/src/lib/task/task-form/components/user-task-cloud-buttons/user-task-cloud-buttons.component.html b/lib/process-services-cloud/src/lib/task/task-form/components/user-task-cloud-buttons/user-task-cloud-buttons.component.html index 73144426e01..7b06233dee9 100644 --- a/lib/process-services-cloud/src/lib/task/task-form/components/user-task-cloud-buttons/user-task-cloud-buttons.component.html +++ b/lib/process-services-cloud/src/lib/task/task-form/components/user-task-cloud-buttons/user-task-cloud-buttons.component.html @@ -1,29 +1,32 @@ diff --git a/lib/process-services-cloud/src/lib/task/task-form/components/user-task-cloud/user-task-cloud.component.html b/lib/process-services-cloud/src/lib/task/task-form/components/user-task-cloud/user-task-cloud.component.html index af825db1e17..e07985f75c7 100644 --- a/lib/process-services-cloud/src/lib/task/task-form/components/user-task-cloud/user-task-cloud.component.html +++ b/lib/process-services-cloud/src/lib/task/task-form/components/user-task-cloud/user-task-cloud.component.html @@ -7,24 +7,31 @@ [appName]="appName" [candidateUsers]="candidateUsers" [candidateGroups]="candidateGroups" - [taskId]="taskId" - [taskDetails]="taskDetails" - [showValidationIcon]="showValidationIcon" - [showTitle]="showTitle" [displayModeConfigurations]="displayModeConfigurations" [fieldValidators]="fieldValidators" + [showValidationIcon]="showValidationIcon" + [showTitle]="showTitle" + [taskId]="taskId" + [taskDetails]="taskDetails" (cancelClick)="onCancelForm()" + (executeOutcome)="onExecuteOutcome($event)" + (error)="onError($event)" (formSaved)="onFormSaved()" + (formContentClicked)="onFormContentClicked($event)" (taskCompleted)="onCompleteTaskForm()" + (taskClaimed)="onClaimTask()" (taskUnclaimed)="onTaskUnclaimed()" - (formContentClicked)="onFormContentClicked($event)" - (executeOutcome)="onExecuteOutcome($event)" - (error)="onError($event)"> + > - + + @@ -48,8 +55,8 @@

@@ -68,7 +75,6 @@

- diff --git a/lib/process-services-cloud/src/lib/task/task-form/components/user-task-cloud/user-task-cloud.component.ts b/lib/process-services-cloud/src/lib/task/task-form/components/user-task-cloud/user-task-cloud.component.ts index 37ef552d0f9..acc0a6a95b5 100644 --- a/lib/process-services-cloud/src/lib/task/task-form/components/user-task-cloud/user-task-cloud.component.ts +++ b/lib/process-services-cloud/src/lib/task/task-form/components/user-task-cloud/user-task-cloud.component.ts @@ -128,6 +128,26 @@ export class UserTaskCloudComponent implements OnInit, OnChanges { private taskCloudService: TaskCloudService = inject(TaskCloudService); private readonly destroyRef = inject(DestroyRef); + ngOnChanges(changes: SimpleChanges) { + const appName = changes['appName']; + if (appName && appName.currentValue !== appName.previousValue && this.taskId) { + this.loadTask(); + return; + } + + const taskId = changes['taskId']; + if (taskId?.currentValue && this.appName) { + this.loadTask(); + return; + } + } + + ngOnInit() { + if (this.appName === '' && this.taskId) { + this.loadTask(); + } + } + canClaimTask(): boolean { return !this.readOnly && this.taskCloudService.canClaimTask(this.taskDetails) && this.hasCandidateUsersOrGroups(); } @@ -140,7 +160,7 @@ export class UserTaskCloudComponent implements OnInit, OnChanges { return !this.readOnly && this.taskCloudService.canUnclaimTask(this.taskDetails) && this.hasCandidateUsersOrGroups(); } - getTaskType() { + getTaskType(): void { if (this.taskDetails && !!this.taskDetails.formKey && this.taskDetails.formKey.includes(this.taskTypeEnum.Form)) { this.taskType = this.taskTypeEnum.Form; } else if (this.taskDetails && !!this.taskDetails.formKey && this.taskDetails.formKey.includes(this.taskTypeEnum.Screen)) { @@ -164,52 +184,52 @@ export class UserTaskCloudComponent implements OnInit, OnChanges { return this.hasCandidateUsers() || this.hasCandidateGroups(); } - onCancelForm() { + onCancelForm(): void { this.cancelClick.emit(); } - onCancelClick() { + onCancelClick(): void { this.cancelClick.emit(this.taskId); } - onClaimTask() { + onClaimTask(): void { this.loadTask(); this.taskClaimed.emit(this.taskId); } - onCompleteTask() { + onCompleteTask(): void { this.loadTask(); this.taskCompleted.emit(this.taskId); } - onCompleteTaskForm() { + onCompleteTaskForm(): void { this.taskCompleted.emit(); } - onError(data: any) { + onError(data: any): void { this.error.emit(data); } - onExecuteOutcome(outcome: FormOutcomeEvent) { + onExecuteOutcome(outcome: FormOutcomeEvent): void { this.executeOutcome.emit(outcome); } - onFormContentClicked(content: ContentLinkModel) { + onFormContentClicked(content: ContentLinkModel): void { this.formContentClicked.emit(content); } - onFormSaved() { + onFormSaved(): void { this.formSaved.emit(); } - onTaskUnclaimed() { + onTaskUnclaimed(): void { this.taskUnclaimed.emit(); } - onUnclaimTask() { + onUnclaimTask(): void { this.loadTask(); this.taskUnclaimed.emit(this.taskId); } - private loadTask() { + private loadTask(): void { this.loading = true; this.taskCloudService .getTaskById(this.appName, this.taskId) @@ -225,29 +245,9 @@ export class UserTaskCloudComponent implements OnInit, OnChanges { this.taskCloudService.getCandidateGroups(this.appName, this.taskId).subscribe((groups) => (this.candidateGroups = groups || [])); } - public switchToDisplayMode(newDisplayMode?: string) { + public switchToDisplayMode(newDisplayMode?: string): void { if (this.adfCloudTaskForm) { this.adfCloudTaskForm.switchToDisplayMode(newDisplayMode); } } - - ngOnChanges(changes: SimpleChanges) { - const appName = changes['appName']; - if (appName && appName.currentValue !== appName.previousValue && this.taskId) { - this.loadTask(); - return; - } - - const taskId = changes['taskId']; - if (taskId?.currentValue && this.appName) { - this.loadTask(); - return; - } - } - - ngOnInit() { - if (this.appName === '' && this.taskId) { - this.loadTask(); - } - } } diff --git a/lib/process-services-cloud/src/lib/task/task-form/task-form.module.ts b/lib/process-services-cloud/src/lib/task/task-form/task-form.module.ts index 75d5b8ad046..cb3fca0743c 100644 --- a/lib/process-services-cloud/src/lib/task/task-form/task-form.module.ts +++ b/lib/process-services-cloud/src/lib/task/task-form/task-form.module.ts @@ -22,12 +22,12 @@ import { FormCloudModule } from '../../form/form-cloud.module'; import { TaskDirectiveModule } from '../directives/task-directive.module'; import { TaskFormCloudComponent } from './components/task-form-cloud/task-form-cloud.component'; import { CoreModule } from '@alfresco/adf-core'; -import { ScreenCloudComponent } from '../../screen/components/screen-cloud/screen-cloud.component'; +import { TaskScreenCloudComponent } from '../../screen/components/screen-cloud/screen-cloud.component'; import { UserTaskCloudComponent } from './components/user-task-cloud/user-task-cloud.component'; import { UserTaskCloudButtonsComponent } from './components/user-task-cloud-buttons/user-task-cloud-buttons.component'; @NgModule({ - imports: [CoreModule, CommonModule, MaterialModule, FormCloudModule, TaskDirectiveModule, ScreenCloudComponent], + imports: [CoreModule, CommonModule, MaterialModule, FormCloudModule, TaskDirectiveModule, TaskScreenCloudComponent], declarations: [TaskFormCloudComponent, UserTaskCloudComponent, UserTaskCloudButtonsComponent], exports: [TaskFormCloudComponent, UserTaskCloudComponent] }) From 69afeddca2fa936be6d50d8b488eb3d56f38e30a Mon Sep 17 00:00:00 2001 From: "tomek.hanaj" Date: Tue, 17 Dec 2024 14:43:41 +0100 Subject: [PATCH 6/8] [AAE-29424] updated import to avoid circular dependency error --- .../components/user-task-cloud/user-task-cloud.component.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/process-services-cloud/src/lib/task/task-form/components/user-task-cloud/user-task-cloud.component.ts b/lib/process-services-cloud/src/lib/task/task-form/components/user-task-cloud/user-task-cloud.component.ts index acc0a6a95b5..d9f58f14133 100644 --- a/lib/process-services-cloud/src/lib/task/task-form/components/user-task-cloud/user-task-cloud.component.ts +++ b/lib/process-services-cloud/src/lib/task/task-form/components/user-task-cloud/user-task-cloud.component.ts @@ -21,7 +21,7 @@ import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; import { FormCloudDisplayModeConfiguration } from '../../../../services/form-fields.interfaces'; import { TaskCloudService } from '../../../services/task-cloud.service'; import { TaskDetailsCloudModel } from '../../../start-task/models/task-details-cloud.model'; -import { TaskFormCloudComponent } from '@alfresco/adf-process-services-cloud'; +import { TaskFormCloudComponent } from '../task-form-cloud/task-form-cloud.component'; const TaskTypes = { Form: 'form', From 5c4fa3aa8e0bae8ecef4ac80fc2b55a29c088609 Mon Sep 17 00:00:00 2001 From: "tomek.hanaj" Date: Wed, 18 Dec 2024 11:06:34 +0100 Subject: [PATCH 7/8] [AAE-29424] updated styles to avoid visual bug --- .../user-task-cloud-buttons/user-task-cloud-buttons.component.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/process-services-cloud/src/lib/task/task-form/components/user-task-cloud-buttons/user-task-cloud-buttons.component.ts b/lib/process-services-cloud/src/lib/task/task-form/components/user-task-cloud-buttons/user-task-cloud-buttons.component.ts index e767bf9a4e1..c5f9ece2186 100644 --- a/lib/process-services-cloud/src/lib/task/task-form/components/user-task-cloud-buttons/user-task-cloud-buttons.component.ts +++ b/lib/process-services-cloud/src/lib/task/task-form/components/user-task-cloud-buttons/user-task-cloud-buttons.component.ts @@ -19,6 +19,7 @@ import { Component, EventEmitter, Input, Output } from '@angular/core'; @Component({ selector: 'adf-cloud-user-task-cloud-buttons', + styles: ['button { margin-right: 8px; }'], templateUrl: './user-task-cloud-buttons.component.html' }) export class UserTaskCloudButtonsComponent { From 254aadf0bca12766824843947b7f1b069a55974c Mon Sep 17 00:00:00 2001 From: "tomek.hanaj" Date: Wed, 18 Dec 2024 13:19:33 +0100 Subject: [PATCH 8/8] [AAE-29424] added self closing tags --- .../task-form-cloud/task-form-cloud.component.html | 3 +-- .../user-task-cloud/user-task-cloud.component.html | 8 +++----- 2 files changed, 4 insertions(+), 7 deletions(-) diff --git a/lib/process-services-cloud/src/lib/task/task-form/components/task-form-cloud/task-form-cloud.component.html b/lib/process-services-cloud/src/lib/task/task-form/components/task-form-cloud/task-form-cloud.component.html index 0b85839fe4c..233ffabda54 100644 --- a/lib/process-services-cloud/src/lib/task/task-form/components/task-form-cloud/task-form-cloud.component.html +++ b/lib/process-services-cloud/src/lib/task/task-form/components/task-form-cloud/task-form-cloud.component.html @@ -33,8 +33,7 @@ (claimTask)="onClaimTask()" (unclaimTask)="onUnclaimTask()" (error)="onError($event)" - > - + /> diff --git a/lib/process-services-cloud/src/lib/task/task-form/components/user-task-cloud/user-task-cloud.component.html b/lib/process-services-cloud/src/lib/task/task-form/components/user-task-cloud/user-task-cloud.component.html index e07985f75c7..536db5439e8 100644 --- a/lib/process-services-cloud/src/lib/task/task-form/components/user-task-cloud/user-task-cloud.component.html +++ b/lib/process-services-cloud/src/lib/task/task-form/components/user-task-cloud/user-task-cloud.component.html @@ -21,8 +21,7 @@ (taskCompleted)="onCompleteTaskForm()" (taskClaimed)="onClaimTask()" (taskUnclaimed)="onTaskUnclaimed()" - > - + /> @@ -30,8 +29,7 @@ [taskId]="taskId" [appName]="appName" [screenId]="screenId" - > - + /> @@ -90,5 +88,5 @@

(claimTask)="onClaimTask()" (unclaimTask)="onUnclaimTask()" (error)="onError($event)" - > + />