AAE-26215 Standalone start-process component (#10531)

This commit is contained in:
Denys Vuika 2025-01-06 12:00:46 -05:00 committed by GitHub
parent 14e7290466
commit 0bc5b6d139
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
12 changed files with 60 additions and 279 deletions

View File

@ -509,7 +509,6 @@ Status: Experimental | [Source](<>) |
| Name | Description | Source link |
| ---- | ----------- | ----------- |
| [Group initial pipe](process-services-cloud/pipes/group-initial.pipe.md) | Extracts the initial character from a group name. | [Source](../lib/process-services-cloud/src/lib/group/pipe/group-initial.pipe.ts) |
| [Process name cloud pipe](process-services-cloud/pipes/process-name-cloud.pipe.md) | When an identifier is specified, the input will be transformed replacing the identifiers with the values of the selected process definition provided. | [Source](../lib/process-services-cloud/src/lib/pipes/process-name-cloud.pipe.ts) |
### Services

View File

@ -1,49 +0,0 @@
---
Title: Process name cloud pipe
Added: v3.9.0
Status: Active
Last reviewed: 2020-06-02
---
# [Process name cloud pipe](../../../lib/process-services-cloud/src/lib/pipes/process-name-cloud.pipe.ts "Defined in process-name-cloud.pipe.ts")
When an identifier is specified, the input will be transformed replacing the identifiers with the values of the selected process definition provided.
## Basic Usage
processNameCloudPipe.transform('Example - %{processDefinition} - %{datetime}', new ProcessDefinitionCloud({ name: 'upload-passport'}));
### Properties
| Name | Type | Default value | Description |
| ---- | ---- | ------------- | ----------- |
| processNameFormat | string | undefined | The process name format including the preferred identifiers to be used |
| selectedProcessDefinition | [`ProcessDefinitionCloud`](../../../lib/process-services-cloud/src/lib/models/process-definition-cloud.model.ts) | undefined | (optional) The selected process definition |
## Details
The pipe offers a convenient way to format a process name using a process name format template.
The supported identifiers that can be used in the process name format are the following:
- %{processDefinition}
- %{datetime}
When the %{processDefinition} identifier is used, the selected process definition provided
will be added and positioned in the same place as the identifier.
When the %{datetime} identifier is used, the current datetime will be added and positioned in the same place as the identifier.
Important Notes:
- All the identifiers are case-insensitive.
- The identifiers can be used in any position (beginning, middle, end, custom).
- The identifiers can NOT be used more than once each in the same processNameFormat (The second occurrence of each identifier will be ignored
and handled as a plain string).
#### Result
```ts
processNameCloudPipe.transform('Example - %{processDefinition} - %{datetime}', new ProcessDefinitionCloud({ name: 'upload-passport'}));
//Returns 'Example - upload passport - June 02, 2020, 12:00:00 AM'
```

View File

@ -1,70 +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 { ProcessNameCloudPipe } from './process-name-cloud.pipe';
import { LocalizedDatePipe, CoreTestingModule } from '@alfresco/adf-core';
import { ProcessInstanceCloud } from '../process/start-process/models/process-instance-cloud.model';
describe('ProcessNameCloudPipe', () => {
let processNamePipe: ProcessNameCloudPipe;
const defaultName = 'default-name';
const datetimeIdentifier = '%{datetime}';
const processDefinitionIdentifier = '%{processDefinition}';
const mockCurrentDate: number = new Date('Wed Oct 23 2019').getTime();
const mockLocalizedCurrentDate = 'Oct 23, 2019, 12:00:00 AM';
const nameWithProcessDefinitionIdentifier = `${defaultName} - ${processDefinitionIdentifier}`;
const nameWithDatetimeIdentifier = `${defaultName} - ${datetimeIdentifier}`;
const nameWithAllIdentifiers = `${defaultName} ${processDefinitionIdentifier} - ${datetimeIdentifier}`;
const fakeProcessInstanceDetails: ProcessInstanceCloud = { processDefinitionName: 'my-process-definition' };
beforeEach(() => {
TestBed.configureTestingModule({
imports: [CoreTestingModule],
providers: [LocalizedDatePipe]
});
const localizedDatePipe = TestBed.inject(LocalizedDatePipe);
processNamePipe = new ProcessNameCloudPipe(localizedDatePipe);
});
it('should not modify the name when there is no identifier', () => {
const transformResult = processNamePipe.transform(defaultName);
expect(transformResult).toEqual(defaultName);
});
it('should add the selected process definition name to the process name', () => {
const transformResult = processNamePipe.transform(nameWithProcessDefinitionIdentifier, fakeProcessInstanceDetails);
expect(transformResult).toEqual(`${defaultName} - ${fakeProcessInstanceDetails.processDefinitionName}`);
});
it('should add the current datetime to the process name', () => {
spyOn(Date.prototype, 'getTime').and.returnValue(mockCurrentDate);
const transformResult = processNamePipe.transform(nameWithDatetimeIdentifier);
expect(transformResult).toEqual(`${defaultName} - ${mockLocalizedCurrentDate}`);
});
it('should add the current datetime and the selected process definition name when both identifiers are present', () => {
spyOn(Date.prototype, 'getTime').and.returnValue(mockCurrentDate);
const transformResult = processNamePipe.transform(nameWithAllIdentifiers, fakeProcessInstanceDetails);
expect(transformResult).toEqual(`${defaultName} ${fakeProcessInstanceDetails.processDefinitionName} - ${mockLocalizedCurrentDate}`);
});
it('should not modify the process name when processDefinition identifier is present but no process definition is selected', () => {
const transformResult = processNamePipe.transform(nameWithProcessDefinitionIdentifier);
expect(transformResult).toEqual(`${defaultName} - `);
});
});

View File

@ -1,50 +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 { Pipe, PipeTransform } from '@angular/core';
import { LocalizedDatePipe } from '@alfresco/adf-core';
import { ProcessInstanceCloud } from '../process/start-process/models/process-instance-cloud.model';
import { getTime } from 'date-fns';
export const DATE_TIME_IDENTIFIER_REG_EXP = new RegExp('%{datetime}', 'i');
export const PROCESS_DEFINITION_IDENTIFIER_REG_EXP = new RegExp('%{processdefinition}', 'i');
@Pipe({ name: 'processNameCloud' })
export class ProcessNameCloudPipe implements PipeTransform {
constructor(private localizedDatePipe: LocalizedDatePipe) {
}
transform(processNameFormat: string, processInstance?: ProcessInstanceCloud): string {
let processName = processNameFormat;
if (processName.match(DATE_TIME_IDENTIFIER_REG_EXP)) {
const presentDateTime = getTime(new Date());
processName = processName.replace(
DATE_TIME_IDENTIFIER_REG_EXP,
this.localizedDatePipe.transform(presentDateTime, 'medium')
);
}
if (processName.match(PROCESS_DEFINITION_IDENTIFIER_REG_EXP)) {
const selectedProcessDefinitionName = processInstance ? processInstance.processDefinitionName : '';
processName = processName.replace(
PROCESS_DEFINITION_IDENTIFIER_REG_EXP,
selectedProcessDefinitionName
);
}
return processName;
}
}

View File

@ -1,30 +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 { NgModule } from '@angular/core';
import { ProcessNameCloudPipe } from './process-name-cloud.pipe';
@NgModule({
declarations: [
ProcessNameCloudPipe
],
exports: [
ProcessNameCloudPipe
]
})
export class ProcessServicesCloudPipeModule {
}

View File

@ -33,7 +33,6 @@ import {
} from './services/public-api';
import { PeopleCloudModule } from './people/people-cloud.module';
import { CloudFormRenderingService } from './form/components/cloud-form-rendering.service';
import { ProcessServicesCloudPipeModule } from './pipes/process-services-cloud-pipe.module';
import { ApolloModule } from 'apollo-angular';
import { RichTextEditorComponent } from './rich-text-editor';
@ -47,7 +46,6 @@ import { RichTextEditorComponent } from './rich-text-editor';
PeopleCloudModule,
FormCloudModule,
TaskFormModule,
ProcessServicesCloudPipeModule,
ApolloModule,
RichTextEditorComponent
],
@ -60,7 +58,6 @@ import { RichTextEditorComponent } from './rich-text-editor';
FormCloudModule,
TaskFormModule,
PeopleCloudModule,
ProcessServicesCloudPipeModule,
RichTextEditorComponent
]
})

View File

@ -18,22 +18,21 @@
import { NgModule } from '@angular/core';
import { ProcessFiltersCloudModule } from './process-filters/process-filters-cloud.module';
import { ProcessListCloudModule } from './process-list/process-list-cloud.module';
import { StartProcessCloudModule } from './start-process/start-process-cloud.module';
import { CoreModule, LocalizedDatePipe } from '@alfresco/adf-core';
import { ProcessHeaderCloudModule } from './process-header/process-header-cloud.module';
import { ProcessDirectiveModule } from './directives/process-directive.module';
import { ProcessNameCloudPipe } from '../pipes/process-name-cloud.pipe';
import { StartProcessCloudComponent } from './start-process/components/start-process-cloud.component';
@NgModule({
imports: [
CoreModule,
ProcessFiltersCloudModule,
ProcessListCloudModule,
StartProcessCloudModule,
StartProcessCloudComponent,
ProcessHeaderCloudModule,
ProcessDirectiveModule
],
exports: [ProcessFiltersCloudModule, ProcessListCloudModule, StartProcessCloudModule, ProcessHeaderCloudModule, ProcessDirectiveModule],
providers: [ProcessNameCloudPipe, LocalizedDatePipe]
exports: [ProcessFiltersCloudModule, ProcessListCloudModule, StartProcessCloudComponent, ProcessHeaderCloudModule, ProcessDirectiveModule],
providers: [LocalizedDatePipe]
})
export class ProcessCloudModule {}

View File

@ -46,7 +46,6 @@ import { By } from '@angular/platform-browser';
import { ProcessPayloadCloud } from '../models/process-payload-cloud.model';
import { ProcessWithFormPayloadCloud } from '../models/process-with-form-payload-cloud.model';
import { ProcessServiceCloudTestingModule } from '../../../testing/process-service-cloud.testing.module';
import { ProcessNameCloudPipe } from '../../../pipes/process-name-cloud.pipe';
import { ProcessInstanceCloud } from '../models/process-instance-cloud.model';
import { ESCAPE } from '@angular/cdk/keycodes';
import { ProcessDefinitionCloud } from '../../../models/process-definition-cloud.model';
@ -999,8 +998,7 @@ describe('StartProcessCloudComponent', () => {
});
it('should set the process name using the processName cloud pipe when a process definition gets selected', async () => {
const processNameCloudPipe = TestBed.inject(ProcessNameCloudPipe);
const processNamePipeTransformSpy = spyOn(processNameCloudPipe, 'transform').and.returnValue('fake-transformed-name');
const getDefaultProcessNameSpy = spyOn(component, 'getDefaultProcessName').and.returnValue('fake-transformed-name');
const expectedProcessInstanceDetails: ProcessInstanceCloud = { processDefinitionName: fakeProcessDefinitions[0].name };
getDefinitionsSpy.and.returnValue(of([fakeProcessDefinitions[0]]));
formDefinitionSpy.and.stub();
@ -1011,7 +1009,7 @@ describe('StartProcessCloudComponent', () => {
fixture.detectChanges();
await fixture.whenStable();
expect(processNamePipeTransformSpy).toHaveBeenCalledWith(component.name, expectedProcessInstanceDetails);
expect(getDefaultProcessNameSpy).toHaveBeenCalledWith(component.name, expectedProcessInstanceDetails);
expect(component.processInstanceName.dirty).toBe(true);
expect(component.processInstanceName.touched).toBe(true);
expect(component.processInstanceName.value).toEqual('fake-transformed-name');
@ -1038,9 +1036,8 @@ describe('StartProcessCloudComponent', () => {
getDefinitionsSpy.and.returnValue(of(definitions));
const processNameCloudPipe = TestBed.inject(ProcessNameCloudPipe);
const fakeTransformedName = 'fake-transformed-name';
spyOn(processNameCloudPipe, 'transform').and.returnValue(fakeTransformedName);
spyOn(component, 'getDefaultProcessName').and.returnValue(fakeTransformedName);
component.processDefinitionName = 'fake-name';
const change = new SimpleChange(null, 'MyApp', true);

View File

@ -29,16 +29,17 @@ import {
ViewChild,
ViewEncapsulation
} from '@angular/core';
import {
ContentLinkModel,
FORM_FIELD_VALIDATORS,
FormFieldValidator,
FormModel,
InplaceFormInputComponent,
LocalizedDatePipe,
TranslationService
} from '@alfresco/adf-core';
import { AbstractControl, FormControl, FormGroup, ValidatorFn, Validators } from '@angular/forms';
import { MatAutocompleteTrigger } from '@angular/material/autocomplete';
import { AbstractControl, FormControl, FormGroup, ReactiveFormsModule, ValidatorFn, Validators } from '@angular/forms';
import { MatAutocompleteModule, MatAutocompleteTrigger } from '@angular/material/autocomplete';
import { catchError, debounceTime } from 'rxjs/operators';
import { ProcessInstanceCloud } from '../models/process-instance-cloud.model';
import { ProcessPayloadCloud } from '../models/process-payload-cloud.model';
@ -47,15 +48,41 @@ import { StartProcessCloudService } from '../services/start-process-cloud.servic
import { forkJoin, of } 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';
import { FormCloudDisplayModeConfiguration } from '../../../services/form-fields.interfaces';
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
import { getTime } from 'date-fns';
import { CommonModule } from '@angular/common';
import { TranslateModule } from '@ngx-translate/core';
import { MatProgressSpinnerModule } from '@angular/material/progress-spinner';
import { MatCardModule } from '@angular/material/card';
import { MatButtonModule } from '@angular/material/button';
import { FormCloudModule } from '../../../form/form-cloud.module';
import { MatIconModule } from '@angular/material/icon';
import { MatInputModule } from '@angular/material/input';
import { MatOptionModule } from '@angular/material/core';
const MAX_NAME_LENGTH: number = 255;
const PROCESS_DEFINITION_DEBOUNCE: number = 300;
const DATE_TIME_IDENTIFIER_REG_EXP = new RegExp('%{datetime}', 'i');
const PROCESS_DEFINITION_IDENTIFIER_REG_EXP = new RegExp('%{processdefinition}', 'i');
@Component({
selector: 'adf-cloud-start-process',
standalone: true,
imports: [
CommonModule,
TranslateModule,
MatProgressSpinnerModule,
MatCardModule,
MatButtonModule,
FormCloudModule,
InplaceFormInputComponent,
MatIconModule,
MatInputModule,
MatOptionModule,
MatAutocompleteModule,
ReactiveFormsModule
],
templateUrl: './start-process-cloud.component.html',
styleUrls: ['./start-process-cloud.component.scss'],
encapsulation: ViewEncapsulation.None
@ -140,7 +167,6 @@ export class StartProcessCloudComponent implements OnChanges, OnInit {
resolvedValues?: TaskVariableCloud[];
customOutcome: string;
isProcessStarting = false;
isFormCloudLoaded = false;
isFormCloudLoading = false;
@ -162,7 +188,7 @@ export class StartProcessCloudComponent implements OnChanges, OnInit {
private readonly destroyRef = inject(DestroyRef);
private readonly startProcessCloudService = inject(StartProcessCloudService);
private readonly processNameCloudPipe = inject(ProcessNameCloudPipe);
private readonly localizedDatePipe = inject(LocalizedDatePipe);
get isProcessFormValid(): boolean {
if (this.hasForm && this.isFormCloudLoaded) {
@ -500,9 +526,23 @@ export class StartProcessCloudComponent implements OnChanges, OnInit {
setDefaultProcessName(processDefinitionName: string): void {
const processInstanceDetails: ProcessInstanceCloud = { processDefinitionName };
const defaultProcessName = this.processNameCloudPipe.transform(this.name, processInstanceDetails);
const defaultProcessName = this.getDefaultProcessName(this.name, processInstanceDetails);
this.processInstanceName.setValue(defaultProcessName);
this.processInstanceName.markAsDirty();
this.processInstanceName.markAsTouched();
}
getDefaultProcessName(processNameFormat: string, processInstance?: ProcessInstanceCloud): string {
let processName = processNameFormat;
if (processName.match(DATE_TIME_IDENTIFIER_REG_EXP)) {
const presentDateTime = getTime(new Date());
processName = processName.replace(DATE_TIME_IDENTIFIER_REG_EXP, this.localizedDatePipe.transform(presentDateTime, 'medium'));
}
if (processName.match(PROCESS_DEFINITION_IDENTIFIER_REG_EXP)) {
const selectedProcessDefinitionName = processInstance ? processInstance.processDefinitionName : '';
processName = processName.replace(PROCESS_DEFINITION_IDENTIFIER_REG_EXP, selectedProcessDefinitionName);
}
return processName;
}
}

View File

@ -16,26 +16,11 @@
*/
import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { FormsModule, ReactiveFormsModule } from '@angular/forms';
import { MaterialModule } from '../../material.module';
import { FormCloudModule } from '../../form/form-cloud.module';
import { StartProcessCloudComponent } from './components/start-process-cloud.component';
import { CoreModule } from '@alfresco/adf-core';
/** @deprecated use StartProcessCloudComponent instead */
@NgModule({
imports: [
FormsModule,
CommonModule,
FormCloudModule,
MaterialModule,
ReactiveFormsModule,
CoreModule
],
declarations: [
StartProcessCloudComponent
],
exports: [
StartProcessCloudComponent
]
imports: [StartProcessCloudComponent],
exports: [StartProcessCloudComponent]
})
export class StartProcessCloudModule {}

View File

@ -1,35 +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 { CardViewItemValidator } from '@alfresco/adf-core';
export class NumericFieldValidator implements CardViewItemValidator {
message: string = 'ADF_CLOUD_TASK_HEADER.FORM_VALIDATION.INVALID_FIELD';
isValid(value: any): boolean {
if (!value) {
return false;
}
return !isNaN(+value) && !this.whitespaces(value);
}
whitespaces(value: any): boolean {
const isWhitespace = (value || '').trim().length === 0;
return !(value.length === 0 || !isWhitespace);
}
}

View File

@ -27,8 +27,6 @@ export * from './lib/rich-text-editor/public-api';
export * from './lib/types';
export * from './lib/common/index';
export * from './lib/pipes/process-name-cloud.pipe';
export * from './lib/pipes/process-services-cloud-pipe.module';
export * from './lib/models/process-definition-cloud.model';
export * from './lib/models/date-cloud-filter.model';
export * from './lib/models/application-version.model';
@ -39,4 +37,4 @@ export * from './lib/models/task-list-sorting.model';
export * from './lib/models/process-instance-variable.model';
export * from './lib/models/variable-definition';
export * from './lib/models/date-format-cloud.model';
export * from './lib/models/process-variable-filter.model'
export * from './lib/models/process-variable-filter.model';