[ACA-3358] Show user friendly error templates when there are no applications/processDefinitions (#5746)

* * Added empty template on start process component

* * Removed errorMessageId

* * Added unit tests to recent changes

* * Added doc

* * fixed comments

* * Used showError notification

* * After rebase

* * Fixed failing e2e

* * Fixed comments
This commit is contained in:
siva kumar
2020-06-09 13:45:22 +05:30
committed by GitHub
parent fffa97c7f8
commit b161ceab26
13 changed files with 361 additions and 187 deletions

View File

@@ -224,7 +224,8 @@
[name]="defaultProcessName" [name]="defaultProcessName"
(formContentClicked)="onContentClick($event)" (formContentClicked)="onContentClick($event)"
(start)="onStartProcessInstance($event)" (start)="onStartProcessInstance($event)"
(cancel)="onCancelProcessInstance()"> (cancel)="onCancelProcessInstance()"
(error)="onStartProcessError($event)">
</adf-start-process> </adf-start-process>
</div> </div>
</div> </div>

View File

@@ -36,7 +36,7 @@ import {
import { import {
FORM_FIELD_VALIDATORS, FormRenderingService, FormService, FORM_FIELD_VALIDATORS, FormRenderingService, FormService,
DynamicTableRow, ValidateDynamicTableRowEvent, AppConfigService, PaginationComponent, UserPreferenceValues, DynamicTableRow, ValidateDynamicTableRowEvent, AppConfigService, PaginationComponent, UserPreferenceValues,
AlfrescoApiService, UserPreferencesService, LogService, DataCellEvent AlfrescoApiService, UserPreferencesService, LogService, DataCellEvent, NotificationService
} from '@alfresco/adf-core'; } from '@alfresco/adf-core';
import { AnalyticsReportListComponent } from '@alfresco/adf-insights'; import { AnalyticsReportListComponent } from '@alfresco/adf-insights';
@@ -174,6 +174,7 @@ export class ProcessServiceComponent implements AfterViewInit, OnDestroy, OnInit
formRenderingService: FormRenderingService, formRenderingService: FormRenderingService,
formService: FormService, formService: FormService,
private location: Location, private location: Location,
private notificationService: NotificationService,
private preferenceService: UserPreferencesService) { private preferenceService: UserPreferencesService) {
this.defaultProcessName = this.appConfig.get<string>('adf-start-process.name'); this.defaultProcessName = this.appConfig.get<string>('adf-start-process.name');
@@ -403,6 +404,10 @@ export class ProcessServiceComponent implements AfterViewInit, OnDestroy, OnInit
this.reloadProcessFilters(); this.reloadProcessFilters();
} }
onStartProcessError(event: any) {
this.notificationService.showError(event.message);
}
isStartProcessMode(): boolean { isStartProcessMode(): boolean {
return this.currentProcessInstanceId === currentProcessIdNew; return this.currentProcessInstanceId === currentProcessIdNew;
} }

View File

@@ -195,6 +195,25 @@ You can use the `showSelectApplicationDropdown` property to Hide or show applica
![Start process with selected application](../../docassets/images/start-process-with-selected-application.png) ![Start process with selected application](../../docassets/images/start-process-with-selected-application.png)
### Error handling
When an error occurs, the component will emit an error event that can be used to handle errors. Example:
```html
<adf-start-process
[appId]="YOUR_APP_ID"
[title]="'ADF_PROCESS_LIST.START_PROCESS.FORM.TITLE'"
[name]="PROCESS_NAME"
(error)="onError($event)">
</adf-start-process>
```
```ts
onError(error) {
this.notificationService.showError(event.response.body.message);
}
```
## See also ## See also
- [Select Apps Dialog component](select-apps-dialog.component.md) - [Select Apps Dialog component](select-apps-dialog.component.md)

View File

@@ -1,5 +1,5 @@
<div class="adf-empty-content"> <div class="adf-empty-content">
<mat-icon class="adf-empty-content__icon">{{ icon }}</mat-icon> <adf-icon class="adf-empty-content__icon" [value]="icon"></adf-icon>
<div class="adf-empty-content__title">{{ title | translate }}</div> <div class="adf-empty-content__title">{{ title | translate }}</div>
<div class="adf-empty-content__subtitle">{{ subtitle | translate }}</div> <div class="adf-empty-content__subtitle">{{ subtitle | translate }}</div>
<ng-content></ng-content> <ng-content></ng-content>

View File

@@ -4,6 +4,7 @@
$config: mat-typography-config(); $config: mat-typography-config();
$foreground: map-get($theme, foreground); $foreground: map-get($theme, foreground);
$adf-empty-content-icon-opacity: 0.6;
.adf-empty-content { .adf-empty-content {
color: mat-color($foreground, text, 0.54); color: mat-color($foreground, text, 0.54);
@@ -12,10 +13,12 @@
align-items: center; align-items: center;
&__icon { &__icon {
.mat-icon {
font-size: mat-font-size($config, display-3); font-size: mat-font-size($config, display-3);
height: mat-font-size($config, display-3) !important; height: mat-font-size($config, display-3) !important;
width: mat-font-size($config, display-3) !important; width: mat-font-size($config, display-3) !important;
} }
}
&__title { &__title {
font-size: 18px; font-size: 18px;
@@ -35,4 +38,8 @@
text-align: center; text-align: center;
} }
} }
.adf-icon {
opacity: $adf-empty-content-icon-opacity;
}
} }

View File

@@ -21,12 +21,14 @@ import { TranslateModule } from '@ngx-translate/core';
import { MaterialModule } from '../material.module'; import { MaterialModule } from '../material.module';
import { ErrorContentComponent } from './error-content/error-content.component'; import { ErrorContentComponent } from './error-content/error-content.component';
import { EmptyContentComponent } from './empty-content/empty-content.component'; import { EmptyContentComponent } from './empty-content/empty-content.component';
import { IconModule } from '../icon/icon.module';
@NgModule({ @NgModule({
imports: [ imports: [
CommonModule, CommonModule,
MaterialModule, MaterialModule,
TranslateModule TranslateModule,
IconModule
], ],
declarations: [ declarations: [
ErrorContentComponent, ErrorContentComponent,

View File

@@ -273,6 +273,8 @@
"START_PROCESS": { "START_PROCESS": {
"BUTTON": "Start Process", "BUTTON": "Start Process",
"NO_PROCESS_DEFINITIONS": "You can't start a process as there are no process definitions available", "NO_PROCESS_DEFINITIONS": "You can't start a process as there are no process definitions available",
"NO_START_FORM": "No start form",
"NO_PROCESS_DEF_SELECTED": "No process definition selected",
"FORM": { "FORM": {
"TITLE": "Start Process", "TITLE": "Start Process",
"LABEL": { "LABEL": {

View File

@@ -1,9 +1,13 @@
<ng-container *ngIf="isLoading(); then showLoadingTemplate; else showStartProcessTemplate"></ng-container>
<ng-template #showLoadingTemplate>
<mat-spinner class="adf-start-process-loading"></mat-spinner>
</ng-template>
<ng-template #showStartProcessTemplate>
<ng-container *ngIf="hasApplications() || hasProcessDefinitions() ; else showEmptyTemplate">
<div class="adf-start-process"> <div class="adf-start-process">
<div class="adf-title" *ngIf="title">{{ title | translate}}</div> <div class="adf-title" *ngIf="title">{{ title | translate}}</div>
<div class="content" *ngIf="isProcessDefinitionEmpty()"> <div class="content">
<div class="subtitle" id="error-message" *ngIf="errorMessageId">
{{errorMessageId|translate}}
</div>
<div class="adf-start-process-definition-container"> <div class="adf-start-process-definition-container">
<mat-form-field *ngIf="showSelectApplicationDropdown" [floatLabel]="'always'" class="adf-start-process-app-list"> <mat-form-field *ngIf="showSelectApplicationDropdown" [floatLabel]="'always'" class="adf-start-process-app-list">
<mat-select <mat-select
@@ -43,7 +47,8 @@
id="adf-select-process-dropdown" id="adf-select-process-dropdown"
*ngIf="showSelectProcessDropdown" *ngIf="showSelectProcessDropdown"
mat-icon-button mat-icon-button
(click)="displayDropdown($event)"> (click)="displayDropdown($event)"
[disabled]="disableDropdownButton()">
<mat-icon>arrow_drop_down</mat-icon> <mat-icon>arrow_drop_down</mat-icon>
</button> </button>
</ng-container> </ng-container>
@@ -71,9 +76,11 @@
</mat-error> </mat-error>
</mat-form-field> </mat-form-field>
<ng-container *ngIf="!isProcessDefinitionsLoading ; else showStartFormLoadingTemplate">
<ng-container *ngIf="isProcessDefinitionSelected() ; else emptyProcessDefTemplate">
<ng-container *ngIf="hasStartForm(); else noStartFormTemplate">
<adf-start-form <adf-start-form
#startForm #startForm
*ngIf="hasStartForm()"
[data]="values" [data]="values"
[disableStartProcessButton]="processNameInput.invalid" [disableStartProcessButton]="processNameInput.invalid"
[processDefinitionId]="selectedProcessDef.id" [processDefinitionId]="selectedProcessDef.id"
@@ -87,11 +94,25 @@
{{'ADF_PROCESS_LIST.START_PROCESS.FORM.ACTION.CANCEL'| translate | uppercase}} {{'ADF_PROCESS_LIST.START_PROCESS.FORM.ACTION.CANCEL'| translate | uppercase}}
</button> </button>
</adf-start-form> </adf-start-form>
</div> </ng-container>
<div class="content" *ngIf="hasErrorMessage()"> <ng-template #noStartFormTemplate>
<div class="subtitle" class="error-message" id="no-process-message"> <adf-empty-content
{{'ADF_PROCESS_LIST.START_PROCESS.NO_PROCESS_DEFINITIONS' | translate | uppercase}} class="adf-start-process-empty-template"
</div> [icon]="'assessment'"
[title]="'ADF_PROCESS_LIST.START_PROCESS.NO_START_FORM' | translate">
</adf-empty-content>
</ng-template>
</ng-container>
<ng-template #emptyProcessDefTemplate>
<adf-empty-content class="adf-start-process-empty-template"
[icon]="'assessment'"
[title]="'ADF_PROCESS_LIST.START_PROCESS.NO_PROCESS_DEF_SELECTED' | translate">
</adf-empty-content>
</ng-template>
</ng-container>
<ng-template #showStartFormLoadingTemplate>
<mat-spinner class="adf-start-process-loading"></mat-spinner>
</ng-template>
</div> </div>
<div class="mat-content-actions" *ngIf="!hasStartForm()"> <div class="mat-content-actions" *ngIf="!hasStartForm()">
<button <button
@@ -114,3 +135,11 @@
</button> </button>
</div> </div>
</div> </div>
</ng-container>
<ng-template #showEmptyTemplate>
<adf-empty-content class="adf-start-process-empty-template"
[icon]="'assessment'"
[title]="'ADF_PROCESS_LIST.START_PROCESS.NO_PROCESS_DEFINITIONS' | translate">
</adf-empty-content>
</ng-template>
</ng-template>

View File

@@ -65,4 +65,9 @@
margin-right: auto; margin-right: auto;
} }
} }
.adf-start-process-loading {
margin-left: calc((100% - 100px) / 2);
margin-right: calc((100% - 100px) / 2);
}
} }

View File

@@ -306,20 +306,6 @@ describe('StartFormComponent', () => {
}); });
}); });
it('should indicate an error to the user if process defs cannot be loaded', async(() => {
getDefinitionsSpy = getDefinitionsSpy.and.returnValue(throwError({}));
component.appId = 123;
const change = new SimpleChange(null, 123, true);
component.ngOnChanges({ 'appId': change });
fixture.detectChanges();
fixture.whenStable().then(() => {
const errorEl = fixture.nativeElement.querySelector('#error-message');
expect(errorEl).not.toBeNull('Expected error message to be present');
expect(errorEl.innerText.trim()).toBe('ADF_PROCESS_LIST.START_PROCESS.ERROR.LOAD_PROCESS_DEFS');
});
}));
it('should show no process available message when no process definition is loaded', async(() => { it('should show no process available message when no process definition is loaded', async(() => {
getDefinitionsSpy = getDefinitionsSpy.and.returnValue(of([])); getDefinitionsSpy = getDefinitionsSpy.and.returnValue(of([]));
component.appId = 123; component.appId = 123;
@@ -328,7 +314,7 @@ describe('StartFormComponent', () => {
fixture.detectChanges(); fixture.detectChanges();
fixture.whenStable().then(() => { fixture.whenStable().then(() => {
const noProcessElement = fixture.nativeElement.querySelector('#no-process-message'); const noProcessElement = fixture.nativeElement.querySelector('.adf-empty-content__title');
expect(noProcessElement).not.toBeNull('Expected no available process message to be present'); expect(noProcessElement).not.toBeNull('Expected no available process message to be present');
expect(noProcessElement.innerText.trim()).toBe('ADF_PROCESS_LIST.START_PROCESS.NO_PROCESS_DEFINITIONS'); expect(noProcessElement.innerText.trim()).toBe('ADF_PROCESS_LIST.START_PROCESS.NO_PROCESS_DEFINITIONS');
}); });
@@ -501,30 +487,6 @@ describe('StartFormComponent', () => {
}); });
})); }));
it('should throw error event when process cannot be started', async(() => {
const errorSpy = spyOn(component.error, 'error');
const error = { message: 'My error' };
startProcessSpy = startProcessSpy.and.returnValue(throwError(error));
component.selectedProcessDef = testProcessDef;
component.startProcess();
fixture.whenStable().then(() => {
expect(errorSpy).toHaveBeenCalledWith(error);
});
}));
it('should indicate an error to the user if process cannot be started', async(() => {
fixture.detectChanges();
startProcessSpy = startProcessSpy.and.returnValue(throwError({}));
component.selectedProcessDef = testProcessDef;
component.startProcess();
fixture.detectChanges();
fixture.whenStable().then(() => {
const errorEl = fixture.nativeElement.querySelector('#error-message');
expect(errorEl).not.toBeNull();
expect(errorEl.innerText.trim()).toBe('ADF_PROCESS_LIST.START_PROCESS.ERROR.START');
});
}));
it('should emit start event when start select a process and add a name', (done) => { it('should emit start event when start select a process and add a name', (done) => {
const disposableStart = component.start.subscribe(() => { const disposableStart = component.start.subscribe(() => {
disposableStart.unsubscribe(); disposableStart.unsubscribe();
@@ -747,4 +709,121 @@ describe('StartFormComponent', () => {
expect(processNameInput.disabled).toEqual(false); expect(processNameInput.disabled).toEqual(false);
}); });
}); });
describe('Empty Template', () => {
it('should show no process definition available template when application/process definitions are empty', async() => {
getDeployedApplicationsSpy = spyOn(appsProcessService, 'getDeployedApplications').and.returnValue(of([]));
getDefinitionsSpy.and.returnValue(of([]));
component.showSelectApplicationDropdown = true;
component.appId = 3;
component.ngOnInit();
fixture.detectChanges();
await fixture.whenStable();
const noProcessElement = fixture.nativeElement.querySelector('.adf-empty-content__title');
expect(noProcessElement).not.toBeNull('Expected no available process message to be present');
expect(noProcessElement.innerText.trim()).toBe('ADF_PROCESS_LIST.START_PROCESS.NO_PROCESS_DEFINITIONS');
});
it('should show no process definition available template if processDefinitions are empty', async() => {
getDefinitionsSpy.and.returnValue(of([]));
component.appId = 3;
component.ngOnInit();
fixture.detectChanges();
await fixture.whenStable();
const noProcessElement = fixture.nativeElement.querySelector('.adf-empty-content__title');
expect(noProcessElement).not.toBeNull('Expected no available process message to be present');
expect(noProcessElement.innerText.trim()).toBe('ADF_PROCESS_LIST.START_PROCESS.NO_PROCESS_DEFINITIONS');
});
it('should show no process definition selected template if there is no process definition selected', async() => {
getDefinitionsSpy.and.returnValue(of(testMultipleProcessDefs));
getDeployedApplicationsSpy = spyOn(appsProcessService, 'getDeployedApplications').and.returnValue(of(deployedApps));
component.showSelectApplicationDropdown = true;
component.appId = 1234;
component.ngOnInit();
fixture.detectChanges();
await fixture.whenStable();
const noProcessElement = fixture.nativeElement.querySelector('.adf-empty-content__title');
expect(noProcessElement).not.toBeNull('Expected no available process message to be present');
expect(noProcessElement.innerText.trim()).toBe('ADF_PROCESS_LIST.START_PROCESS.NO_PROCESS_DEF_SELECTED');
});
it('should show no start form template if selected process definition does not have start form', async() => {
getDefinitionsSpy.and.returnValue(of(testMultipleProcessDefs));
getDeployedApplicationsSpy = spyOn(appsProcessService, 'getDeployedApplications').and.returnValue(of(deployedApps));
component.showSelectApplicationDropdown = true;
component.processDefinitionName = 'My Process 1';
component.appId = 3;
component.ngOnInit();
fixture.detectChanges();
await fixture.whenStable();
const noProcessElement = fixture.nativeElement.querySelector('.adf-empty-content__title');
expect(noProcessElement).not.toBeNull('Expected no available process message to be present');
expect(noProcessElement.innerText.trim()).toBe('ADF_PROCESS_LIST.START_PROCESS.NO_START_FORM');
});
});
describe('Error event', () => {
const processDefError = { message: 'Failed to load Process definitions' };
const applicationsError = { message: 'Failed to load applications' };
const startProcessError = { message: 'Failed to start process' };
beforeEach(() => {
fixture.detectChanges();
});
it('should emit error event in case loading process definitions failed', async() => {
const errorSpy = spyOn(component.error, 'emit');
getDefinitionsSpy.and.returnValue(throwError(processDefError));
component.appId = 3;
component.ngOnInit();
fixture.detectChanges();
await fixture.whenStable();
expect(errorSpy).toHaveBeenCalledWith(processDefError);
});
it('should emit error event in case loading applications failed', async() => {
const errorSpy = spyOn(component.error, 'emit');
getDeployedApplicationsSpy = spyOn(appsProcessService, 'getDeployedApplications').and.returnValue(throwError(applicationsError));
component.showSelectApplicationDropdown = true;
component.appId = 3;
component.ngOnInit();
fixture.detectChanges();
await fixture.whenStable();
expect(errorSpy).toHaveBeenCalledWith(applicationsError);
});
it('should emit error event in case start process failed', async() => {
const errorSpy = spyOn(component.error, 'emit');
getDefinitionsSpy.and.returnValue(of(testMultipleProcessDefs));
getDeployedApplicationsSpy = spyOn(appsProcessService, 'getDeployedApplications').and.returnValue(of(deployedApps));
startProcessSpy.and.returnValue(throwError(startProcessError));
component.showSelectApplicationDropdown = true;
component.processDefinitionName = 'My Process 1';
component.name = 'mock name';
component.appId = 3;
component.ngOnInit();
fixture.detectChanges();
await fixture.whenStable();
component.startProcess();
fixture.detectChanges();
expect(errorSpy).toHaveBeenCalledWith(startProcessError);
});
});
}); });

View File

@@ -93,11 +93,11 @@ export class StartProcessInstanceComponent implements OnChanges, OnInit, OnDestr
/** Emitted when the process is canceled. */ /** Emitted when the process is canceled. */
@Output() @Output()
cancel: EventEmitter<ProcessInstance> = new EventEmitter<ProcessInstance>(); cancel: EventEmitter<void> = new EventEmitter<void>();
/** Emitted when an error occurs. */ /** Emitted when an error occurs. */
@Output() @Output()
error: EventEmitter<ProcessInstance> = new EventEmitter<ProcessInstance>(); error: EventEmitter<any> = new EventEmitter<any>();
/** Emitted when process definition selection changes. */ /** Emitted when process definition selection changes. */
@Output() @Output()
@@ -115,7 +115,6 @@ export class StartProcessInstanceComponent implements OnChanges, OnInit, OnDestr
processDefinitions: ProcessDefinitionRepresentation[] = []; processDefinitions: ProcessDefinitionRepresentation[] = [];
selectedProcessDef: ProcessDefinitionRepresentation; selectedProcessDef: ProcessDefinitionRepresentation;
errorMessageId: string = '';
processNameInput: FormControl; processNameInput: FormControl;
processDefinitionInput: FormControl; processDefinitionInput: FormControl;
filteredProcessesDefinitions$: Observable<ProcessDefinitionRepresentation[]>; filteredProcessesDefinitions$: Observable<ProcessDefinitionRepresentation[]>;
@@ -123,7 +122,10 @@ export class StartProcessInstanceComponent implements OnChanges, OnInit, OnDestr
alfrescoRepositoryName: string; alfrescoRepositoryName: string;
applications: AppDefinitionRepresentationModel[] = []; applications: AppDefinitionRepresentationModel[] = [];
selectedApplication: AppDefinitionRepresentationModel; selectedApplication: AppDefinitionRepresentationModel;
isProcessDefinitionsLoading = true; isProcessDefinitionsLoading = true;
isAppsLoading = true;
private onDestroy$ = new Subject<boolean>(); private onDestroy$ = new Subject<boolean>();
constructor(private activitiProcess: ProcessService, constructor(private activitiProcess: ProcessService,
private activitiContentService: ActivitiContentService, private activitiContentService: ActivitiContentService,
@@ -219,7 +221,6 @@ export class StartProcessInstanceComponent implements OnChanges, OnInit, OnDestr
loadProcessDefinitions(appId: any): void { loadProcessDefinitions(appId: any): void {
this.isProcessDefinitionsLoading = true; this.isProcessDefinitionsLoading = true;
this.resetSelectedProcessDefinition(); this.resetSelectedProcessDefinition();
this.resetErrorMessage();
this.activitiProcess.getProcessDefinitions(appId).pipe( this.activitiProcess.getProcessDefinitions(appId).pipe(
map((processDefinitionRepresentations: ProcessDefinitionRepresentation[]) => { map((processDefinitionRepresentations: ProcessDefinitionRepresentation[]) => {
@@ -248,9 +249,9 @@ export class StartProcessInstanceComponent implements OnChanges, OnInit, OnDestr
this.processDefinitionSelection.emit(this.selectedProcessDef); this.processDefinitionSelection.emit(this.selectedProcessDef);
this.isProcessDefinitionsLoading = false; this.isProcessDefinitionsLoading = false;
}, },
() => { (error) => {
this.errorMessageId = 'ADF_PROCESS_LIST.START_PROCESS.ERROR.LOAD_PROCESS_DEFS';
this.isProcessDefinitionsLoading = false; this.isProcessDefinitionsLoading = false;
this.error.emit(error);
}); });
} }
@@ -269,6 +270,7 @@ export class StartProcessInstanceComponent implements OnChanges, OnInit, OnDestr
} }
loadApps() { loadApps() {
this.isAppsLoading = true;
this.appsProcessService this.appsProcessService
.getDeployedApplications() .getDeployedApplications()
.pipe(map((response: AppDefinitionRepresentationModel[]) => { .pipe(map((response: AppDefinitionRepresentationModel[]) => {
@@ -293,20 +295,32 @@ export class StartProcessInstanceComponent implements OnChanges, OnInit, OnDestr
this.selectedApplication = filteredApps.currentApplication; this.selectedApplication = filteredApps.currentApplication;
this.applicationSelection.emit(this.selectedApplication); this.applicationSelection.emit(this.selectedApplication);
this.toggleProcessNameAndDefinitionsDropdown(); this.toggleProcessNameAndDefinitionsDropdown();
this.loadProcessDefinitions(this.selectedApplication ? this.selectedApplication.id : null); this.isAppsLoading = false;
this.loadProcessDefinitionsBasedOnSelectedApp();
}, },
err => { (err) => {
this.isAppsLoading = false;
this.error.emit(err); this.error.emit(err);
} }
); );
} }
loadProcessDefinitionsBasedOnSelectedApp() {
if (this.selectedApplication && this.selectedApplication.id) {
this.loadProcessDefinitions(this.selectedApplication ? this.selectedApplication.id : null);
} else {
this.isProcessDefinitionsLoading = false;
this.resetProcessDefinitions();
}
}
onAppSelectionChange(selectedApplication: any) { onAppSelectionChange(selectedApplication: any) {
this.resetProcessDefinitions();
this.selectedApplication = selectedApplication.value; this.selectedApplication = selectedApplication.value;
this.applicationSelection.emit(this.selectedApplication); this.applicationSelection.emit(this.selectedApplication);
this.toggleProcessNameAndDefinitionsDropdown(); this.toggleProcessNameAndDefinitionsDropdown();
this.loadProcessDefinitions(this.selectedApplication.id); this.loadProcessDefinitionsBasedOnSelectedApp();
} }
private isAppSelected(): boolean { private isAppSelected(): boolean {
@@ -315,13 +329,28 @@ export class StartProcessInstanceComponent implements OnChanges, OnInit, OnDestr
private removeDefaultApps(apps: AppDefinitionRepresentationModel []): AppDefinitionRepresentationModel[] { private removeDefaultApps(apps: AppDefinitionRepresentationModel []): AppDefinitionRepresentationModel[] {
return apps.filter((app) => app.id); return apps.filter((app) => app.id);
}
hasApplications(): boolean {
return this.applications && this.applications.length > 0;
}
hasProcessDefinitions(): boolean {
return this.processDefinitions && this.processDefinitions.length > 0;
}
isProcessDefinitionSelected(): boolean {
return !!(this.selectedProcessDef && this.selectedProcessDef.id);
} }
isProcessDefinitionsEmpty(): boolean { isProcessDefinitionsEmpty(): boolean {
return this.processDefinitions.length === 0; return this.processDefinitions.length === 0;
} }
disableDropdownButton(): boolean {
return this.showSelectApplicationDropdown && !this.isAppSelected();
}
getAlfrescoRepositoryName(): string { getAlfrescoRepositoryName(): string {
let alfrescoRepositoryName = this.appConfig.get<string>(AppConfigValues.ALFRESCO_REPOSITORY_NAME); let alfrescoRepositoryName = this.appConfig.get<string>(AppConfigValues.ALFRESCO_REPOSITORY_NAME);
if (!alfrescoRepositoryName) { if (!alfrescoRepositoryName) {
@@ -345,7 +374,6 @@ export class StartProcessInstanceComponent implements OnChanges, OnInit, OnDestr
startProcess(outcome?: string) { startProcess(outcome?: string) {
if (this.selectedProcessDef && this.selectedProcessDef.id && this.name) { if (this.selectedProcessDef && this.selectedProcessDef.id && this.name) {
this.resetErrorMessage();
const formValues = this.startForm ? this.startForm.form.values : undefined; const formValues = this.startForm ? this.startForm.form.values : undefined;
this.activitiProcess.startProcess(this.selectedProcessDef.id, this.name, outcome, formValues, this.variables).subscribe( this.activitiProcess.startProcess(this.selectedProcessDef.id, this.name, outcome, formValues, this.variables).subscribe(
(res) => { (res) => {
@@ -353,8 +381,7 @@ export class StartProcessInstanceComponent implements OnChanges, OnInit, OnDestr
this.start.emit(res); this.start.emit(res);
}, },
(err) => { (err) => {
this.errorMessageId = 'ADF_PROCESS_LIST.START_PROCESS.ERROR.START'; this.error.emit(err);
this.error.error(err);
} }
); );
} }
@@ -368,11 +395,6 @@ export class StartProcessInstanceComponent implements OnChanges, OnInit, OnDestr
return this.selectedProcessDef && this.selectedProcessDef.hasStartForm; return this.selectedProcessDef && this.selectedProcessDef.hasStartForm;
} }
isProcessDefinitionEmpty(): boolean {
const hasErrorMessage = this.errorMessageId ? true : false;
return this.processDefinitions ? (this.processDefinitions.length > 0 || hasErrorMessage) : hasErrorMessage;
}
isStartFormMissingOrValid(): boolean { isStartFormMissingOrValid(): boolean {
if (this.startForm) { if (this.startForm) {
return this.startForm.form && this.startForm.form.isValid; return this.startForm.form && this.startForm.form.isValid;
@@ -386,15 +408,15 @@ export class StartProcessInstanceComponent implements OnChanges, OnInit, OnDestr
} }
private resetSelectedProcessDefinition() { private resetSelectedProcessDefinition() {
this.selectedProcessDef = new ProcessDefinitionRepresentation(); this.selectedProcessDef = undefined;
if (this.processDefinitionInput) {
this.processDefinitionInput.setValue('');
}
} }
private resetErrorMessage(): void { private resetProcessDefinitions() {
this.errorMessageId = ''; this.processDefinitions = [];
} this.resetSelectedProcessDefinition();
hasErrorMessage(): boolean {
return this.processDefinitions.length === 0 && !this.errorMessageId;
} }
public onOutcomeClick(outcome: string) { public onOutcomeClick(outcome: string) {
@@ -407,7 +429,6 @@ export class StartProcessInstanceComponent implements OnChanges, OnInit, OnDestr
if (this.startForm) { if (this.startForm) {
this.startForm.data = {}; this.startForm.data = {};
} }
this.resetErrorMessage();
} }
displayFn(process: any): string { displayFn(process: any): string {
@@ -453,4 +474,8 @@ export class StartProcessInstanceComponent implements OnChanges, OnInit, OnDestr
this.selectedProcessDef = processDefinition; this.selectedProcessDef = processDefinition;
this.processDefinitionSelection.emit(this.selectedProcessDef); this.processDefinitionSelection.emit(this.selectedProcessDef);
} }
isLoading(): boolean {
return this.showSelectApplicationDropdown ? this.isAppsLoading : false;
}
} }

View File

@@ -29,7 +29,7 @@ export class TaskFormCloudComponent {
claimButton: ElementFinder = element(by.css('button[adf-cloud-claim-task]')); claimButton: ElementFinder = element(by.css('button[adf-cloud-claim-task]'));
form: ElementFinder = element(by.css('adf-cloud-form')); form: ElementFinder = element(by.css('adf-cloud-form'));
formTitle: ElementFinder = element(by.css(`span.adf-form-title`)); formTitle: ElementFinder = element(by.css(`span.adf-form-title`));
emptyContentIcon: ElementFinder = element(by.css(`div.adf-empty-content mat-icon.adf-empty-content__icon`)); emptyContentIcon: ElementFinder = element(by.css(`div.adf-empty-content adf-icon.adf-empty-content__icon`));
emptyContentTitle: ElementFinder = element(by.css(`div.adf-empty-content div.adf-empty-content__title`)); emptyContentTitle: ElementFinder = element(by.css(`div.adf-empty-content div.adf-empty-content__title`));
emptyContentSubtitle: ElementFinder = element(by.css(`div.adf-empty-content div.adf-empty-content__subtitle`)); emptyContentSubtitle: ElementFinder = element(by.css(`div.adf-empty-content div.adf-empty-content__subtitle`));
readOnlyForm = element(by.css('div[class="adf-readonly-form"]')); readOnlyForm = element(by.css('div[class="adf-readonly-form"]'));

View File

@@ -30,7 +30,7 @@ export class StartProcessPage {
cancelProcessButton: ElementFinder = element(by.id('cancel_process')); cancelProcessButton: ElementFinder = element(by.id('cancel_process'));
formStartProcessButton: ElementFinder = element(by.css('button[data-automation-id="adf-form-start process"]')); formStartProcessButton: ElementFinder = element(by.css('button[data-automation-id="adf-form-start process"]'));
startProcessButton: ElementFinder = element(by.css('button[data-automation-id="btn-start"]')); startProcessButton: ElementFinder = element(by.css('button[data-automation-id="btn-start"]'));
noProcess: ElementFinder = element(by.id('no-process-message')); noProcess: ElementFinder = element(by.css('.adf-empty-content__title'));
processDefinition: ElementFinder = element(by.css('input[id="processDefinitionName"]')); processDefinition: ElementFinder = element(by.css('input[id="processDefinitionName"]'));
processDefinitionOptionsPanel: ElementFinder = element(by.css('div[class*="mat-autocomplete-panel"]')); processDefinitionOptionsPanel: ElementFinder = element(by.css('div[class*="mat-autocomplete-panel"]'));