diff --git a/lib/process-services-cloud/src/lib/process/start-process/components/start-process-cloud.component.html b/lib/process-services-cloud/src/lib/process/start-process/components/start-process-cloud.component.html index b697d0e4561..154da753f25 100755 --- a/lib/process-services-cloud/src/lib/process/start-process/components/start-process-cloud.component.html +++ b/lib/process-services-cloud/src/lib/process/start-process/components/start-process-cloud.component.html @@ -1,7 +1,6 @@ - + - @@ -12,7 +11,7 @@ {{ errorMessageId | translate }} -
+
- + { fixture.detectChanges(); const startBtn = fixture.nativeElement.querySelector('#button-start'); - expect(component.isProcessFormValid()).toBe(true); + expect(component.isProcessFormValid).toBe(true); expect(startBtn.disabled).toBe(false); })); @@ -154,7 +154,7 @@ describe('StartProcessCloudComponent', () => { expect(component.processDefinitionCurrent.name).toBe(JSON.parse(JSON.stringify(fakeProcessDefinitions[1])).name); const startBtn = fixture.nativeElement.querySelector('#button-start'); - expect(component.isProcessFormValid()).toBe(true); + expect(component.isProcessFormValid).toBe(true); expect(startBtn.disabled).toBe(false); }); @@ -167,7 +167,7 @@ describe('StartProcessCloudComponent', () => { const startBtn = fixture.nativeElement.querySelector('#button-start'); expect(startBtn.disabled).toBe(true); - expect(component.isProcessFormValid()).toBe(false); + expect(component.isProcessFormValid).toBe(false); }); it('should have start button disabled when name not filled out', async () => { @@ -179,7 +179,7 @@ describe('StartProcessCloudComponent', () => { const startBtn = fixture.nativeElement.querySelector('#button-start'); expect(startBtn.disabled).toBe(true); - expect(component.isProcessFormValid()).toBe(false); + expect(component.isProcessFormValid).toBe(false); }); it('should include the static input mappings in the resolved values', fakeAsync(() => { @@ -334,7 +334,7 @@ describe('StartProcessCloudComponent', () => { const startBtn = fixture.nativeElement.querySelector('#button-start'); expect(startBtn.disabled).toBe(false); - expect(component.isProcessFormValid()).toBe(true); + expect(component.isProcessFormValid).toBe(true); }); it('should have start button enabled when default values are set', async () => { @@ -444,7 +444,7 @@ describe('StartProcessCloudComponent', () => { await fixture.whenStable(); const processForm = fixture.nativeElement.querySelector('adf-cloud-form'); - expect(component.hasForm()).toBeTruthy(); + expect(component.hasForm).toBeTruthy(); expect(processForm).not.toBeNull(); }); @@ -713,7 +713,7 @@ describe('StartProcessCloudComponent', () => { await fixture.whenStable(); const processForm = fixture.nativeElement.querySelector('adf-cloud-form'); - expect(component.hasForm()).toBeTruthy(); + expect(component.hasForm).toBeTruthy(); expect(processForm).not.toBeNull(); const payload: ProcessPayloadCloud = new ProcessPayloadCloud({ @@ -896,7 +896,6 @@ describe('StartProcessCloudComponent', () => { }); it('should hide title', () => { - component.loading$.next(false); component.showTitle = false; fixture.detectChanges(); @@ -906,7 +905,7 @@ describe('StartProcessCloudComponent', () => { }); it('should show title', () => { - component.loading$.next(false); + component.processDefinitionLoaded = true; fixture.detectChanges(); const title = fixture.debugElement.query(By.css('.adf-title')); @@ -915,7 +914,7 @@ describe('StartProcessCloudComponent', () => { }); it('should show process definition dropdown', () => { - component.loading$.next(false); + component.processDefinitionLoaded = true; component.processDefinitionList = fakeProcessDefinitions; fixture.detectChanges(); @@ -925,7 +924,7 @@ describe('StartProcessCloudComponent', () => { }); it('should hide process definition dropdown', () => { - component.loading$.next(false); + component.processDefinitionLoaded = true; component.processDefinitionList = fakeProcessDefinitions; component.showSelectProcessDropdown = false; fixture.detectChanges(); @@ -936,7 +935,7 @@ describe('StartProcessCloudComponent', () => { }); it('should show the loading spinner before process definitions loaded', () => { - component.loading$.next(true); + component.processDefinitionLoaded = false; fixture.detectChanges(); const spinner = fixture.debugElement.query(By.css('.adf-loading')); @@ -945,7 +944,7 @@ describe('StartProcessCloudComponent', () => { }); it('should show the process card after process definitions loaded', () => { - component.loading$.next(false); + component.processDefinitionLoaded = true; fixture.detectChanges(); const card = fixture.debugElement.query(By.css('.adf-start-process')); @@ -964,31 +963,45 @@ describe('StartProcessCloudComponent', () => { component.processDefinitionName = fakeProcessDefinitions[0].name; }); - it('start process button should be enabled when isLoading is false', async () => { + it('start process button should be enabled when isProcessStarting is false', async () => { fixture.detectChanges(); component.processForm.controls['processInstanceName'].setValue(fakeProcessDefinitions[0].id); component.appName = 'test app name'; - component.isLoading = false; + component.isProcessStarting = false; fixture.detectChanges(); await fixture.whenStable(); const startButton = fixture.debugElement.query(By.css('#button-start')); expect(startButton).not.toBeNull(); - expect(component.disableStartButton()).toBeFalse(); + expect(component.disableStartButton).toBeFalse(); expect((startButton.nativeElement as HTMLButtonElement).disabled).toBeFalse(); }); + it('start process button should be disabled when isFormCloudLoading is true', async () => { + fixture.detectChanges(); + component.processForm.controls['processInstanceName'].setValue(fakeProcessDefinitions[0].id); + component.appName = 'test app name'; + component.isFormCloudLoading = true; + fixture.detectChanges(); + await fixture.whenStable(); + + const startButton = fixture.debugElement.query(By.css('#button-start')); + expect(startButton).not.toBeNull(); + expect(component.disableStartButton).toBeTrue(); + expect((startButton.nativeElement as HTMLButtonElement).disabled).toBeTrue(); + }); + it('start process button should be disabled when isLoading is true', async () => { fixture.detectChanges(); component.processForm.controls['processInstanceName'].setValue(fakeProcessDefinitions[0].id); component.appName = 'test app name'; - component.isLoading = true; + component.isProcessStarting = true; fixture.detectChanges(); await fixture.whenStable(); const startButton = fixture.debugElement.query(By.css('#button-start')); expect(startButton).not.toBeNull(); - expect(component.disableStartButton()).toBeTrue(); + expect(component.disableStartButton).toBeTrue(); expect((startButton.nativeElement as HTMLButtonElement).disabled).toBeTrue(); }); }); diff --git a/lib/process-services-cloud/src/lib/process/start-process/components/start-process-cloud.component.ts b/lib/process-services-cloud/src/lib/process/start-process/components/start-process-cloud.component.ts index 56e80557aec..34d87e6ab67 100755 --- a/lib/process-services-cloud/src/lib/process/start-process/components/start-process-cloud.component.ts +++ b/lib/process-services-cloud/src/lib/process/start-process/components/start-process-cloud.component.ts @@ -19,6 +19,7 @@ import { Component, EventEmitter, HostListener, + inject, Input, OnChanges, OnDestroy, @@ -30,13 +31,13 @@ import { } from '@angular/core'; import { ContentLinkModel, FORM_FIELD_VALIDATORS, FormFieldValidator, FormModel } from '@alfresco/adf-core'; -import { AbstractControl, UntypedFormBuilder, UntypedFormControl, UntypedFormGroup, ValidatorFn, Validators } from '@angular/forms'; +import { AbstractControl, FormControl, FormGroup, ValidatorFn, Validators } from '@angular/forms'; import { MatAutocompleteTrigger } from '@angular/material/autocomplete'; -import { debounceTime, takeUntil, tap } from 'rxjs/operators'; +import { debounceTime, takeUntil } from 'rxjs/operators'; import { ProcessInstanceCloud } from '../models/process-instance-cloud.model'; import { ProcessPayloadCloud } from '../models/process-payload-cloud.model'; import { StartProcessCloudService } from '../services/start-process-cloud.service'; -import { BehaviorSubject, Subject } from 'rxjs'; +import { Subject } from 'rxjs'; import { ProcessDefinitionCloud } from '../../../models/process-definition-cloud.model'; import { TaskVariableCloud } from '../../../form/models/task-variable-cloud.model'; import { ProcessNameCloudPipe } from '../../../pipes/process-name-cloud.pipe'; @@ -115,39 +116,65 @@ export class StartProcessCloudComponent implements OnChanges, OnInit, OnDestroy processDefinitionSelection: EventEmitter = new EventEmitter(); processDefinitionList: ProcessDefinitionCloud[] = []; - processDefinitionCurrent: ProcessDefinitionCloud; + processDefinitionCurrent?: ProcessDefinitionCloud; errorMessageId: string = ''; - processForm: UntypedFormGroup; processPayloadCloud = new ProcessPayloadCloud(); filteredProcesses: ProcessDefinitionCloud[] = []; - isLoading = false; - isFormCloudLoaded = false; - formCloud: FormModel; staticMappings: TaskVariableCloud[] = []; - resolvedValues: TaskVariableCloud[]; + resolvedValues?: TaskVariableCloud[]; protected onDestroy$ = new Subject(); + + isProcessStarting = false; + isFormCloudLoaded = false; + isFormCloudLoading = false; processDefinitionLoaded = false; - loading$ = new BehaviorSubject(!this.processDefinitionLoaded); - constructor( - private startProcessCloudService: StartProcessCloudService, - private formBuilder: UntypedFormBuilder, - private processNameCloudPipe: ProcessNameCloudPipe - ) {} + formCloud?: FormModel; + processForm = new FormGroup({ + processInstanceName: new FormControl('', [ + Validators.required, + Validators.maxLength(this.getMaxNameLength()), + Validators.pattern('^[^\\s]+(\\s+[^\\s]+)*$') + ]), + processDefinition: new FormControl('', [Validators.required, this.processDefinitionNameValidator()]) + }); + + private readonly startProcessCloudService = inject(StartProcessCloudService); + private readonly processNameCloudPipe = inject(ProcessNameCloudPipe); + + get isProcessFormValid(): boolean { + if (this.hasForm && this.isFormCloudLoaded) { + return (this.formCloud ? !Object.keys(this.formCloud.values).length : false) || this.formCloud?.isValid || this.isProcessStarting; + } else { + return this.processForm.valid || this.isProcessStarting; + } + } + + get disableStartButton(): boolean { + return !this.appName || !this.processDefinition.valid || this.isProcessStarting || this.isFormCloudLoading; + } + + get isProcessDefinitionsEmpty(): boolean { + return !this.processDefinitionList.length; + } + + get processInstanceName(): FormControl { + return this.processForm.controls.processInstanceName; + } + + get processDefinition(): FormControl { + return this.processForm.controls.processDefinition; + } + + get hasForm(): boolean { + return !!this.processDefinitionCurrent?.formKey; + } ngOnInit() { this.initFieldValidators(); - this.processForm = this.formBuilder.group({ - processInstanceName: new UntypedFormControl('', [ - Validators.required, - Validators.maxLength(this.getMaxNameLength()), - Validators.pattern('^[^\\s]+(\\s+[^\\s]+)*$') - ]), - processDefinition: new UntypedFormControl(this.processDefinitionName, [Validators.required, this.processDefinitionNameValidator()]) - }); - + this.processDefinition.setValue(this.processDefinitionName); this.processDefinition.valueChanges .pipe(debounceTime(PROCESS_DEFINITION_DEBOUNCE)) .pipe(takeUntil(this.onDestroy$)) @@ -175,10 +202,6 @@ export class StartProcessCloudComponent implements OnChanges, OnInit, OnDestroy event.stopPropagation(); } - hasForm(): boolean { - return this.processDefinitionCurrent && !!this.processDefinitionCurrent.formKey; - } - onFormLoaded(form: FormModel) { this.isFormCloudLoaded = true; this.formCloud = form; @@ -195,12 +218,13 @@ export class StartProcessCloudComponent implements OnChanges, OnInit, OnDestroy private selectProcessDefinitionByProcessDefinitionName(processDefinitionName: string): void { this.filteredProcesses = this.getProcessDefinitionListByNameOrKey(processDefinitionName); - if (this.isProcessFormValid() && this.filteredProcesses && this.filteredProcesses.length === 1) { + if (this.isProcessFormValid && this.filteredProcesses && this.filteredProcesses.length === 1) { this.setProcessDefinitionOnForm(this.filteredProcesses[0].name); } } setProcessDefinitionOnForm(selectedProcessDefinitionName: string) { + this.isFormCloudLoading = true; const processDefinitionCurrent = this.filteredProcesses.find( (process: ProcessDefinitionCloud) => process.name === selectedProcessDefinitionName || process.key === selectedProcessDefinitionName ); @@ -210,10 +234,12 @@ export class StartProcessCloudComponent implements OnChanges, OnInit, OnDestroy this.staticMappings = staticMappings; this.resolvedValues = this.staticMappings.concat(this.values || []); this.processDefinitionCurrent = processDefinitionCurrent; + this.isFormCloudLoading = false; }, () => { this.resolvedValues = this.values; this.processDefinitionCurrent = processDefinitionCurrent; + this.isFormCloudLoading = false; } ); @@ -254,13 +280,7 @@ export class StartProcessCloudComponent implements OnChanges, OnInit, OnDestroy this.startProcessCloudService .getProcessDefinitions(this.appName) - .pipe( - tap(() => { - this.processDefinitionLoaded = true; - this.loading$.next(false); - }), - takeUntil(this.onDestroy$) - ) + .pipe(takeUntil(this.onDestroy$)) .subscribe( (processDefinitionRepresentations: ProcessDefinitionCloud[]) => { this.processDefinitionList = processDefinitionRepresentations; @@ -276,6 +296,8 @@ export class StartProcessCloudComponent implements OnChanges, OnInit, OnDestroy this.processDefinitionSelectionChanged(processDefinition); } } + + this.processDefinitionLoaded = true; }, () => { this.errorMessageId = 'ADF_CLOUD_PROCESS_LIST.ADF_CLOUD_START_PROCESS.ERROR.LOAD_PROCESS_DEFS'; @@ -287,14 +309,6 @@ export class StartProcessCloudComponent implements OnChanges, OnInit, OnDestroy return !!name; } - isProcessFormValid(): boolean { - if (this.hasForm() && this.isFormCloudLoaded) { - return (this.formCloud ? !Object.keys(this.formCloud.values).length : false) || this.formCloud?.isValid || this.isLoading; - } else { - return this.processForm.valid || this.isLoading; - } - } - private getProcessDefinition(processDefinitionCloud: ProcessDefinitionCloud, processDefinitionName: string): boolean { return ( (this.isValidName(processDefinitionCloud.name) && @@ -303,28 +317,24 @@ export class StartProcessCloudComponent implements OnChanges, OnInit, OnDestroy ); } - isProcessDefinitionsEmpty(): boolean { - return this.processDefinitionList.length === 0; - } - buildProcessCloudPayload() { this.processPayloadCloud.name = this.processInstanceName.value; if (this.variables) { this.processPayloadCloud.variables = this.variables; } - if (this.hasForm()) { + if (this.hasForm) { this.processPayloadCloud.variables = Object.assign(this.processPayloadCloud.variables, this.formCloud.values); } } startProcess() { - this.isLoading = true; + this.isProcessStarting = true; let payloadVariables = {}; if (this.variables) { payloadVariables = this.variables; } - if (this.hasForm()) { + if (this.hasForm) { payloadVariables = Object.assign(payloadVariables, this.formCloud.values); } const createPayload = new ProcessPayloadCloud({ @@ -335,12 +345,12 @@ export class StartProcessCloudComponent implements OnChanges, OnInit, OnDestroy this.startProcessCloudService.startProcess(this.appName, createPayload).subscribe( (res) => { this.success.emit(res); - this.isLoading = false; + this.isProcessStarting = false; }, (err) => { this.errorMessageId = 'ADF_CLOUD_PROCESS_LIST.ADF_CLOUD_START_PROCESS.ERROR.START'; this.error.emit(err); - this.isLoading = false; + this.isProcessStarting = false; } ); } @@ -398,14 +408,6 @@ export class StartProcessCloudComponent implements OnChanges, OnInit, OnDestroy return process.name ? process.name : process.key; } - get processInstanceName(): UntypedFormControl { - return this.processForm.get('processInstanceName') as UntypedFormControl; - } - - get processDefinition(): AbstractControl { - return this.processForm.get('processDefinition'); - } - onFormContentClicked(content: ContentLinkModel) { this.formContentClicked.emit(content); } @@ -429,8 +431,4 @@ export class StartProcessCloudComponent implements OnChanges, OnInit, OnDestroy this.onDestroy$.next(true); this.onDestroy$.complete(); } - - disableStartButton(): boolean { - return !this.appName || !this.processDefinition.valid || this.isLoading; - } }