AAE-26215 standalone cloud tasks (#10537)

This commit is contained in:
Denys Vuika
2025-01-14 12:00:07 -05:00
committed by GitHub
parent 972566fb29
commit af3ca02347
128 changed files with 1419 additions and 1658 deletions

View File

@@ -15,15 +15,7 @@
* limitations under the License.
*/
import { NgModule } from '@angular/core';
import { AppDetailsCloudComponent } from './components/app-details-cloud/app-details-cloud.component';
import { AppListCloudComponent } from './components/app-list-cloud/app-list-cloud.component';
export const APP_LIST_CLOUD_DIRECTIVES = [AppListCloudComponent, AppDetailsCloudComponent] as const;
/** @deprecated use `...APP_LIST_CLOUD_DIRECTIVES` instead */
@NgModule({
imports: [...APP_LIST_CLOUD_DIRECTIVES],
exports: [...APP_LIST_CLOUD_DIRECTIVES]
})
export class AppListCloudModule {}

View File

@@ -37,7 +37,7 @@ describe('DateRangeFilterComponent', () => {
beforeEach(() => {
TestBed.configureTestingModule({
imports: [ProcessServiceCloudTestingModule]
imports: [ProcessServiceCloudTestingModule, DateRangeFilterComponent]
});
fixture = TestBed.createComponent(DateRangeFilterComponent);
component = fixture.componentInstance;

View File

@@ -18,7 +18,6 @@
import { applicationConfig, Meta, moduleMetadata, StoryFn } from '@storybook/angular';
import { ProcessServicesCloudStoryModule } from '../../testing/process-services-cloud-story.module';
import { mockFilterProperty } from '../mock/date-range-filter.mock';
import { ProcessCommonModule } from '../process-common.module';
import { DateRangeFilterComponent } from './date-range-filter.component';
import { importProvidersFrom } from '@angular/core';
@@ -27,7 +26,7 @@ export default {
title: 'Process Services Cloud/Process Common/Date Range Filter',
decorators: [
moduleMetadata({
imports: [ProcessCommonModule]
imports: [DateRangeFilterComponent]
}),
applicationConfig({
providers: [importProvidersFrom(ProcessServicesCloudStoryModule)]

View File

@@ -16,13 +16,17 @@
*/
import { Component, Input, EventEmitter, Output, OnInit } from '@angular/core';
import { MatSelectChange } from '@angular/material/select';
import { MatSelectChange, MatSelectModule } from '@angular/material/select';
import { ProcessFilterProperties, ProcessFilterOptions } from '../../process/process-filters/models/process-filter-cloud.model';
import { FormGroup, FormControl } from '@angular/forms';
import { FormGroup, FormControl, ReactiveFormsModule } from '@angular/forms';
import { DateRangeFilter, DateCloudFilterType } from '../../models/date-cloud-filter.model';
import { endOfDay, isValid, startOfDay } from 'date-fns';
import { DateAdapter, MAT_DATE_FORMATS } from '@angular/material/core';
import { ADF_DATE_FORMATS, AdfDateFnsAdapter } from '@alfresco/adf-core';
import { CommonModule } from '@angular/common';
import { TranslateModule } from '@ngx-translate/core';
import { MatDatepickerModule } from '@angular/material/datepicker';
import { MatFormFieldModule } from '@angular/material/form-field';
interface DateRangeFormProps {
from: FormControl<Date>;
@@ -31,6 +35,8 @@ interface DateRangeFormProps {
@Component({
selector: 'adf-cloud-date-range-filter',
standalone: true,
imports: [CommonModule, TranslateModule, MatDatepickerModule, MatFormFieldModule, ReactiveFormsModule, MatSelectModule],
styleUrls: ['./date-range-filter.component.scss'],
templateUrl: './date-range-filter.component.html',
providers: [

View File

@@ -1,32 +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 { CoreModule } from '@alfresco/adf-core';
import { DateRangeFilterComponent } from './date-range-filter/date-range-filter.component';
import { MaterialModule } from '../material.module';
import { CommonModule } from '@angular/common';
import { DateRangeFilterService } from './date-range-filter/date-range-filter.service';
import { ReactiveFormsModule } from '@angular/forms';
@NgModule({
declarations: [DateRangeFilterComponent],
imports: [CommonModule, CoreModule, MaterialModule, ReactiveFormsModule],
exports: [DateRangeFilterComponent],
providers: [DateRangeFilterService]
})
export class ProcessCommonModule {}

View File

@@ -52,7 +52,7 @@ import {
} from '@alfresco/adf-core';
import { FormCloudService } from '../services/form-cloud.service';
import { TaskVariableCloud } from '../models/task-variable-cloud.model';
import { TaskDetailsCloudModel } from '../../task/start-task/models/task-details-cloud.model';
import { TaskDetailsCloudModel } from '../../task/models/task-details-cloud.model';
import { MatDialog } from '@angular/material/dialog';
import { v4 as uuidGeneration } from 'uuid';
import { FormCloudDisplayMode, FormCloudDisplayModeConfiguration } from '../../services/form-fields.interfaces';

View File

@@ -24,14 +24,14 @@ import { IdentityGroupModel } from '../../../../group/models/identity-group.mode
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
import { CommonModule } from '@angular/common';
import { TranslateModule } from '@ngx-translate/core';
import { GroupCloudModule } from '../../../../group/group-cloud.module';
import { GroupCloudComponent } from '../../../../group/components/group-cloud.component';
/* eslint-disable @angular-eslint/component-selector */
@Component({
selector: 'group-cloud-widget',
standalone: true,
imports: [CommonModule, TranslateModule, ErrorWidgetComponent, GroupCloudModule],
imports: [CommonModule, TranslateModule, ErrorWidgetComponent, GroupCloudComponent],
templateUrl: './group-cloud.widget.html',
host: {
'(click)': 'event($event)',

View File

@@ -25,14 +25,14 @@ import { IdentityUserService } from '../../../../people/services/identity-user.s
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
import { CommonModule } from '@angular/common';
import { TranslateModule } from '@ngx-translate/core';
import { PeopleCloudModule } from '../../../../people/people-cloud.module';
import { PeopleCloudComponent } from '../../../../people/components/people-cloud.component';
/* eslint-disable @angular-eslint/component-selector */
@Component({
selector: 'people-cloud-widget',
standalone: true,
imports: [CommonModule, TranslateModule, ErrorWidgetComponent, PeopleCloudModule],
imports: [CommonModule, TranslateModule, ErrorWidgetComponent, PeopleCloudComponent],
templateUrl: './people-cloud.widget.html',
host: {
'(click)': 'event($event)',

View File

@@ -17,19 +17,26 @@
import { UploadApi } from '@alfresco/js-api';
import { FormFieldOption, FormModel, FormValues } from '@alfresco/adf-core';
import { TaskDetailsCloudModel } from '../../task/start-task/models/task-details-cloud.model';
import { TaskDetailsCloudModel } from '../../task/models/task-details-cloud.model';
import { TaskVariableCloud } from '../models/task-variable-cloud.model';
import { FormContent } from '../../services/form-fields.interfaces';
import { Observable } from 'rxjs';
export interface FormCloudServiceInterface {
uploadApi: UploadApi;
getTaskForm(appName: string, taskId: string, version?: number): Observable<any>;
saveTaskForm(appName: string, taskId: string, processInstanceId: string, formId: string, values: FormValues): Observable<TaskDetailsCloudModel>;
createTemporaryRawRelatedContent(file: any, nodeId: string, contentHost: string): Observable<any>;
completeTaskForm(appName: string, taskId: string, processInstanceId: string, formId: string, formValues: FormValues, outcome: string, version: number): Observable<TaskDetailsCloudModel>;
completeTaskForm(
appName: string,
taskId: string,
processInstanceId: string,
formId: string,
formValues: FormValues,
outcome: string,
version: number
): Observable<TaskDetailsCloudModel>;
getTask(appName: string, taskId: string): Observable<TaskDetailsCloudModel>;
getTaskVariables(appName: string, taskId: string): Observable<TaskVariableCloud[]>;
getForm(appName: string, formKey: string, version?: number): Observable<FormContent>;

View File

@@ -19,7 +19,7 @@ import { Injectable } from '@angular/core';
import { FormValues, FormModel, FormFieldOption } from '@alfresco/adf-core';
import { Observable, from, EMPTY } from 'rxjs';
import { expand, map, reduce, switchMap } from 'rxjs/operators';
import { TaskDetailsCloudModel } from '../../task/start-task/models/task-details-cloud.model';
import { TaskDetailsCloudModel } from '../../task/models/task-details-cloud.model';
import { CompleteFormRepresentation, UploadApi } from '@alfresco/js-api';
import { TaskVariableCloud } from '../models/task-variable-cloud.model';
import { BaseCloudService } from '../../services/base-cloud.service';
@@ -31,16 +31,13 @@ import { AdfHttpClient } from '@alfresco/adf-core/api';
providedIn: 'root'
})
export class FormCloudService extends BaseCloudService implements FormCloudServiceInterface {
private _uploadApi: UploadApi;
get uploadApi(): UploadApi {
this._uploadApi = this._uploadApi ?? new UploadApi(this.apiService.getInstance());
return this._uploadApi;
}
constructor(
adfHttpClient: AdfHttpClient
) {
constructor(adfHttpClient: AdfHttpClient) {
super(adfHttpClient);
}
@@ -54,20 +51,22 @@ export class FormCloudService extends BaseCloudService implements FormCloudServi
*/
getTaskForm(appName: string, taskId: string, version?: number): Observable<any> {
return this.getTask(appName, taskId).pipe(
switchMap(task => this.getForm(appName, task.formKey, version).pipe(
map((form: FormContent) => {
const flattenForm = {
...form.formRepresentation,
...form.formRepresentation.formDefinition,
taskId: task.id,
taskName: task.name,
processDefinitionId: task.processDefinitionId,
processInstanceId: task.processInstanceId
};
delete flattenForm.formDefinition;
return flattenForm;
})
))
switchMap((task) =>
this.getForm(appName, task.formKey, version).pipe(
map((form: FormContent) => {
const flattenForm = {
...form.formRepresentation,
...form.formRepresentation.formDefinition,
taskId: task.id,
taskName: task.name,
processDefinitionId: task.processDefinitionId,
processInstanceId: task.processInstanceId
};
delete flattenForm.formDefinition;
return flattenForm;
})
)
)
);
}
@@ -89,26 +88,15 @@ export class FormCloudService extends BaseCloudService implements FormCloudServi
processInstanceId
};
return this.post(apiUrl, saveFormRepresentation).pipe(
map((res: any) => res.entry)
);
return this.post(apiUrl, saveFormRepresentation).pipe(map((res: any) => res.entry));
}
createTemporaryRawRelatedContent(file: any, nodeId: string, contentHost: string): Observable<any> {
const changedConfig = this.apiService.lastConfig;
changedConfig.provider = 'ALL';
changedConfig.hostEcm = contentHost.replace('/alfresco', '');
this.apiService.getInstance().setConfig(changedConfig);
return from(this.uploadApi.uploadFile(
file,
'',
nodeId,
null,
{ overwrite: true }
)).pipe(
map((res: any) => res.entry)
);
return from(this.uploadApi.uploadFile(file, '', nodeId, null, { overwrite: true })).pipe(map((res: any) => res.entry));
}
/**
@@ -123,7 +111,15 @@ export class FormCloudService extends BaseCloudService implements FormCloudServi
* @param version of the form
* @returns Updated task details
*/
completeTaskForm(appName: string, taskId: string, processInstanceId: string, formId: string, formValues: FormValues, outcome: string, version: number): Observable<TaskDetailsCloudModel> {
completeTaskForm(
appName: string,
taskId: string,
processInstanceId: string,
formId: string,
formValues: FormValues,
outcome: string,
version: number
): Observable<TaskDetailsCloudModel> {
const apiUrl = `${this.getBasePath(appName)}/form/v1/forms/${formId}/submit/versions/${version}`;
const completeFormRepresentation = {
values: formValues,
@@ -135,9 +131,7 @@ export class FormCloudService extends BaseCloudService implements FormCloudServi
completeFormRepresentation.outcome = outcome;
}
return this.post(apiUrl, completeFormRepresentation).pipe(
map((res: any) => res.entry)
);
return this.post(apiUrl, completeFormRepresentation).pipe(map((res: any) => res.entry));
}
/**
@@ -150,9 +144,7 @@ export class FormCloudService extends BaseCloudService implements FormCloudServi
getTask(appName: string, taskId: string): Observable<TaskDetailsCloudModel> {
const apiUrl = `${this.getBasePath(appName)}/query/v1/tasks/${taskId}`;
return this.get(apiUrl).pipe(
map((res: any) => res.entry)
);
return this.get(apiUrl).pipe(map((res: any) => res.entry));
}
/**
@@ -170,10 +162,12 @@ export class FormCloudService extends BaseCloudService implements FormCloudServi
return this.get(apiUrl, { maxItems, skipCount }).pipe(
expand((res: any) => {
skipCount += maxItems;
return res.list.pagination.hasMoreItems ? this.get(apiUrl, {
maxItems,
skipCount
}) : EMPTY;
return res.list.pagination.hasMoreItems
? this.get(apiUrl, {
maxItems,
skipCount
})
: EMPTY;
}),
map((res: any) => res.list.entries.map((variable) => new TaskVariableCloud(variable.entry))),
reduce((acc, res) => acc.concat(res), [])
@@ -221,7 +215,7 @@ export class FormCloudService extends BaseCloudService implements FormCloudServi
delete flattenForm.formDefinition;
const formValues: FormValues = {};
(data || []).forEach(variable => {
(data || []).forEach((variable) => {
formValues[variable.name] = variable.value;
});

View File

@@ -43,7 +43,7 @@
class="adf-cloud-group-row"
id="adf-group-{{i}}"
data-automation-id="adf-cloud-group-row">
<button class="adf-group-short-name" mat-fab>{{group | groupNameInitial }}</button>
<button class="adf-group-short-name" mat-fab>{{getGroupNameInitials(group)}}</button>
<span>{{group.name}}</span>
</div>
</mat-option>

View File

@@ -18,7 +18,6 @@
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { By } from '@angular/platform-browser';
import { of } from 'rxjs';
import { GroupCloudModule } from '../group-cloud.module';
import { GroupCloudComponent } from './group-cloud.component';
import { CoreTestingModule } from '@alfresco/adf-core';
import { DebugElement, SimpleChange } from '@angular/core';
@@ -72,7 +71,7 @@ describe('GroupCloudComponent', () => {
beforeEach(() => {
TestBed.configureTestingModule({
imports: [CoreTestingModule, GroupCloudModule]
imports: [CoreTestingModule, GroupCloudComponent]
});
fixture = TestBed.createComponent(GroupCloudComponent);
component = fixture.componentInstance;

View File

@@ -16,7 +16,6 @@
*/
import { applicationConfig, Meta, moduleMetadata, StoryFn } from '@storybook/angular';
import { GroupCloudModule } from '../group-cloud.module';
import { GroupCloudComponent } from './group-cloud.component';
import { ProcessServicesCloudStoryModule } from '../../testing/process-services-cloud-story.module';
import { IdentityGroupService } from '../services/identity-group.service';
@@ -28,13 +27,10 @@ export default {
title: 'Process Services Cloud/Group Cloud/Group Cloud',
decorators: [
moduleMetadata({
imports: [GroupCloudModule]
imports: [GroupCloudComponent]
}),
applicationConfig({
providers: [
{ provide: IdentityGroupService, useClass: IdentityGroupServiceMock },
importProvidersFrom(ProcessServicesCloudStoryModule)
]
providers: [{ provide: IdentityGroupService, useClass: IdentityGroupServiceMock }, importProvidersFrom(ProcessServicesCloudStoryModule)]
})
],
argTypes: {
@@ -102,7 +98,7 @@ export default {
description: 'FormControl to list of group.',
table: {
type: { summary: 'FormControl' },
defaultValue: { summary: 'new FormControl({ value: \'\', disabled: false })' },
defaultValue: { summary: "new FormControl({ value: '', disabled: false })" },
category: 'Form Controls'
}
},
@@ -111,7 +107,7 @@ export default {
description: 'FormControl to search the group.',
table: {
type: { summary: 'FormControl' },
defaultValue: { summary: 'new FormControl({ value: \'\', disabled: false })' },
defaultValue: { summary: "new FormControl({ value: '', disabled: false })" },
category: 'Form Controls'
}
},

View File

@@ -20,7 +20,6 @@ import {
DestroyRef,
ElementRef,
EventEmitter,
Inject,
inject,
Input,
OnChanges,
@@ -30,18 +29,41 @@ import {
ViewChild,
ViewEncapsulation
} from '@angular/core';
import { UntypedFormControl } from '@angular/forms';
import { ReactiveFormsModule, UntypedFormControl } from '@angular/forms';
import { animate, state, style, transition, trigger } from '@angular/animations';
import { BehaviorSubject, Observable } from 'rxjs';
import { debounceTime, distinctUntilChanged, filter, mergeMap, switchMap, tap } from 'rxjs/operators';
import { ComponentSelectionMode } from '../../types';
import { IdentityGroupModel } from '../models/identity-group.model';
import { IdentityGroupServiceInterface } from '../services/identity-group.service.interface';
import { IDENTITY_GROUP_SERVICE_TOKEN } from '../services/identity-group-service.token';
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
import { CommonModule } from '@angular/common';
import { TranslateModule } from '@ngx-translate/core';
import { MatIconModule } from '@angular/material/icon';
import { MatFormFieldModule } from '@angular/material/form-field';
import { MatProgressBarModule } from '@angular/material/progress-bar';
import { MatSelectModule } from '@angular/material/select';
import { MatAutocompleteModule } from '@angular/material/autocomplete';
import { MatButtonModule } from '@angular/material/button';
import { MatInputModule } from '@angular/material/input';
import { MatChipsModule } from '@angular/material/chips';
import { IdentityGroupService } from '../services/identity-group.service';
@Component({
selector: 'adf-cloud-group',
standalone: true,
imports: [
CommonModule,
TranslateModule,
MatIconModule,
MatFormFieldModule,
MatProgressBarModule,
MatSelectModule,
MatAutocompleteModule,
MatButtonModule,
ReactiveFormsModule,
MatInputModule,
MatChipsModule
],
templateUrl: './group-cloud.component.html',
styleUrls: ['./group-cloud.component.scss'],
animations: [
@@ -140,10 +162,7 @@ export class GroupCloudComponent implements OnInit, OnChanges {
private readonly destroyRef = inject(DestroyRef);
constructor(
@Inject(IDENTITY_GROUP_SERVICE_TOKEN)
private identityGroupService: IdentityGroupServiceInterface
) {}
constructor(private identityGroupService: IdentityGroupService) {}
ngOnInit(): void {
this.initSearch();
@@ -470,4 +489,13 @@ export class GroupCloudComponent implements OnInit, OnChanges {
getValidationMinLength(): string {
return this.searchGroupsControl.errors.minlength.requiredLength;
}
getGroupNameInitials(group: IdentityGroupModel): string {
let result = '';
if (group) {
const groupName = group.name;
result = (groupName ? groupName[0] : '').toUpperCase();
}
return result;
}
}

View File

@@ -16,21 +16,11 @@
*/
import { NgModule } from '@angular/core';
import { FormsModule, ReactiveFormsModule } from '@angular/forms';
import { CommonModule } from '@angular/common';
import { CoreModule } from '@alfresco/adf-core';
import { MaterialModule } from '../material.module';
import { GroupCloudComponent } from './components/group-cloud.component';
import { InitialGroupNamePipe } from './pipe/group-initial.pipe';
import { IDENTITY_GROUP_SERVICE_TOKEN } from './services/identity-group-service.token';
import { IdentityGroupService } from './services/identity-group.service';
import { MatProgressBarModule } from '@angular/material/progress-bar';
/** @deprecated use GroupCloudComponent instead */
@NgModule({
imports: [CommonModule, MaterialModule, FormsModule, ReactiveFormsModule, CoreModule, MatProgressBarModule],
declarations: [GroupCloudComponent, InitialGroupNamePipe],
providers: [{ provide: IDENTITY_GROUP_SERVICE_TOKEN, useExisting: IdentityGroupService }],
exports: [GroupCloudComponent, InitialGroupNamePipe]
imports: [GroupCloudComponent],
exports: [GroupCloudComponent]
})
export class GroupCloudModule {}

View File

@@ -18,30 +18,20 @@
import { Injectable } from '@angular/core';
import { Observable, EMPTY, of } from 'rxjs';
import { IdentityGroupModel } from '../models/identity-group.model';
import { IdentityGroupFilterInterface } from '../services/identity-group-filter.interface';
import { IdentityGroupServiceInterface } from '../services/identity-group.service.interface';
import { IdentityGroupService } from '@alfresco/adf-process-services-cloud';
export const mockVegetableAubergine: IdentityGroupModel = { id: 'aubergine', name: 'Vegetable Aubergine'};
export const mockMeatChicken: IdentityGroupModel = { id: 'chicken', name: 'Meat Chicken'};
export const mockVegetableAubergine: IdentityGroupModel = { id: 'aubergine', name: 'Vegetable Aubergine' };
export const mockMeatChicken: IdentityGroupModel = { id: 'chicken', name: 'Meat Chicken' };
export const mockFoodGroups = [ mockVegetableAubergine, mockMeatChicken ];
export const mockFoodGroups = [mockVegetableAubergine, mockMeatChicken];
export const mockSearchGroupEmptyFilters: IdentityGroupFilterInterface = {
roles: [],
withinApplication: ''
};
@Injectable({
providedIn: 'root'
})
export class IdentityGroupServiceMock implements IdentityGroupServiceInterface {
search(name: string, _filters?: IdentityGroupFilterInterface): Observable<IdentityGroupModel[]> {
@Injectable()
export class IdentityGroupServiceMock extends IdentityGroupService {
search(name: string): Observable<IdentityGroupModel[]> {
if (name.trim() === '') {
return EMPTY;
}
return of(mockFoodGroups.filter(group =>
group.name.toUpperCase().includes(name.toUpperCase())
));
return of(mockFoodGroups.filter((group) => group.name.toUpperCase().includes(name.toUpperCase())));
}
}

View File

@@ -1,39 +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 { HttpErrorResponse } from '@angular/common/http';
import { IdentityGroupFilterInterface } from '../services/identity-group-filter.interface';
export const mockSearchGroupByRoles: IdentityGroupFilterInterface = {
roles: ['fake-role-1', 'fake-role-2'],
withinApplication: ''
};
export const mockSearchGroupByRolesAndApp: IdentityGroupFilterInterface = {
roles: ['fake-role-1', 'fake-role-2'],
withinApplication: 'fake-app-name'
};
export const mockSearchGroupByApp: IdentityGroupFilterInterface = {
roles: [],
withinApplication: 'fake-app-name'
};
export const mockHttpErrorResponse = new HttpErrorResponse({
error: 'Mock Error',
status: 404, statusText: 'Not Found'
});

View File

@@ -1,41 +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 { IdentityGroupModel } from '../models/identity-group.model';
import { InitialGroupNamePipe } from './group-initial.pipe';
describe('InitialGroupNamePipe', () => {
let pipe: InitialGroupNamePipe;
let fakeGroup: IdentityGroupModel;
beforeEach(() => {
pipe = new InitialGroupNamePipe();
fakeGroup = {name: 'mock'};
});
it('should return with the group initial', () => {
fakeGroup.name = 'FAKE-GROUP-NAME';
const result = pipe.transform(fakeGroup);
expect(result).toBe('F');
});
it('should return an empty string when group is null', () => {
const result = pipe.transform(null);
expect(result).toBe('');
});
});

View File

@@ -1,38 +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 { IdentityGroupModel } from '../models/identity-group.model';
@Pipe({
name: 'groupNameInitial'
})
export class InitialGroupNamePipe implements PipeTransform {
transform(group: IdentityGroupModel): string {
let result = '';
if (group) {
result = this.getInitialGroupName(group.name).toUpperCase();
}
return result;
}
getInitialGroupName(groupName: string) {
groupName = (groupName ? groupName[0] : '');
return groupName;
}
}

View File

@@ -16,8 +16,6 @@
*/
export * from './components/group-cloud.component';
export * from './pipe/group-initial.pipe';
export * from './models/identity-group.model';
export * from './group-cloud.module';
export * from './services/identity-group.service';
export * from './services/identity-group-service.token';

View File

@@ -1,21 +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.
*/
export interface IdentityGroupFilterInterface {
roles?: string[];
withinApplication?: string;
}

View File

@@ -1,21 +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 { InjectionToken } from '@angular/core';
import { IdentityGroupServiceInterface } from './identity-group.service.interface';
export const IDENTITY_GROUP_SERVICE_TOKEN = new InjectionToken<IdentityGroupServiceInterface>('IdentityGroup');

View File

@@ -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 { Observable } from 'rxjs';
import { IdentityGroupModel } from '../models/identity-group.model';
import { IdentityGroupFilterInterface } from './identity-group-filter.interface';
export interface IdentityGroupServiceInterface {
search(name: string, filters?: IdentityGroupFilterInterface): Observable<IdentityGroupModel[]>;
}

View File

@@ -18,15 +18,16 @@
import { TestBed } from '@angular/core/testing';
import { TranslateModule } from '@ngx-translate/core';
import { IdentityGroupService } from './identity-group.service';
import {
mockHttpErrorResponse,
mockSearchGroupByApp,
mockSearchGroupByRoles,
mockSearchGroupByRolesAndApp
} from '../mock/identity-group.service.mock';
import { mockFoodGroups } from '../mock/group-cloud.mock';
import { AdfHttpClient } from '@alfresco/adf-core/api';
import { HttpClientTestingModule } from '@angular/common/http/testing';
import { HttpErrorResponse } from '@angular/common/http';
const mockHttpErrorResponse = new HttpErrorResponse({
error: 'Mock Error',
status: 404,
statusText: 'Not Found'
});
describe('IdentityGroupService', () => {
let service: IdentityGroupService;
@@ -81,86 +82,111 @@ describe('IdentityGroupService', () => {
requestSpy.and.returnValue(Promise.resolve(mockFoodGroups));
const searchSpy = spyOn(service, 'search').and.callThrough();
service.search('fake', mockSearchGroupByRoles).subscribe((res) => {
expect(res).toBeDefined();
expect(searchSpy).toHaveBeenCalled();
expect(service.queryParams).toEqual({
search: 'fake',
role: 'fake-role-1,fake-role-2'
service
.search('fake', {
roles: ['fake-role-1', 'fake-role-2'],
withinApplication: ''
})
.subscribe((res) => {
expect(res).toBeDefined();
expect(searchSpy).toHaveBeenCalled();
expect(service.queryParams).toEqual({
search: 'fake',
role: 'fake-role-1,fake-role-2'
});
done();
});
done();
});
});
it('should not fetch groups by roles if error occurred', (done) => {
requestSpy.and.returnValue(Promise.reject(mockHttpErrorResponse));
const searchSpy = spyOn(service, 'search').and.callThrough();
service.search('fake', mockSearchGroupByRoles).subscribe(
() => {
fail('expected an error, not groups');
},
(error) => {
expect(searchSpy).toHaveBeenCalled();
expect(service.queryParams).toEqual({
search: 'fake',
role: 'fake-role-1,fake-role-2'
});
expect(error.status).toEqual(404);
expect(error.statusText).toEqual('Not Found');
expect(error.error).toEqual('Mock Error');
done();
}
);
service
.search('fake', {
roles: ['fake-role-1', 'fake-role-2'],
withinApplication: ''
})
.subscribe(
() => {
fail('expected an error, not groups');
},
(error) => {
expect(searchSpy).toHaveBeenCalled();
expect(service.queryParams).toEqual({
search: 'fake',
role: 'fake-role-1,fake-role-2'
});
expect(error.status).toEqual(404);
expect(error.statusText).toEqual('Not Found');
expect(error.error).toEqual('Mock Error');
done();
}
);
});
it('should fetch groups within app', (done) => {
requestSpy.and.returnValue(Promise.resolve(mockFoodGroups));
service.search('fake', mockSearchGroupByApp).subscribe((res) => {
expect(res).toBeDefined();
expect(service.queryParams).toEqual({
search: 'fake',
application: 'fake-app-name'
service
.search('fake', {
roles: [],
withinApplication: 'fake-app-name'
})
.subscribe((res) => {
expect(res).toBeDefined();
expect(service.queryParams).toEqual({
search: 'fake',
application: 'fake-app-name'
});
done();
});
done();
});
});
it('should fetch groups within app with roles', (done) => {
requestSpy.and.returnValue(Promise.resolve(mockFoodGroups));
service.search('fake', mockSearchGroupByRolesAndApp).subscribe((res) => {
expect(res).toBeDefined();
expect(service.queryParams).toEqual({
search: 'fake',
application: 'fake-app-name',
role: 'fake-role-1,fake-role-2'
service
.search('fake', {
roles: ['fake-role-1', 'fake-role-2'],
withinApplication: 'fake-app-name'
})
.subscribe((res) => {
expect(res).toBeDefined();
expect(service.queryParams).toEqual({
search: 'fake',
application: 'fake-app-name',
role: 'fake-role-1,fake-role-2'
});
done();
});
done();
});
});
it('should not fetch groups within app if error occurred', (done) => {
requestSpy.and.returnValue(Promise.reject(mockHttpErrorResponse));
const searchSpy = spyOn(service, 'search').and.callThrough();
service.search('fake', mockSearchGroupByApp).subscribe(
() => {
fail('expected an error, not groups');
},
(error) => {
expect(searchSpy).toHaveBeenCalled();
expect(service.queryParams).toEqual({
search: 'fake',
application: 'fake-app-name'
});
expect(error.status).toEqual(404);
expect(error.statusText).toEqual('Not Found');
expect(error.error).toEqual('Mock Error');
done();
}
);
service
.search('fake', {
roles: [],
withinApplication: 'fake-app-name'
})
.subscribe(
() => {
fail('expected an error, not groups');
},
(error) => {
expect(searchSpy).toHaveBeenCalled();
expect(service.queryParams).toEqual({
search: 'fake',
application: 'fake-app-name'
});
expect(error.status).toEqual(404);
expect(error.statusText).toEqual('Not Found');
expect(error.error).toEqual('Mock Error');
done();
}
);
});
});
});

View File

@@ -18,21 +18,20 @@
import { Injectable } from '@angular/core';
import { AppConfigService, OAuth2Service } from '@alfresco/adf-core';
import { EMPTY, Observable } from 'rxjs';
import { IdentityGroupServiceInterface } from './identity-group.service.interface';
import { IdentityGroupFilterInterface } from './identity-group-filter.interface';
import { IdentityGroupModel } from '../models/identity-group.model';
const IDENTITY_MICRO_SERVICE_INGRESS = 'identity-adapter-service';
export interface IdentityGroupFilterInterface {
roles?: string[];
withinApplication?: string;
}
@Injectable({ providedIn: 'root' })
export class IdentityGroupService implements IdentityGroupServiceInterface {
export class IdentityGroupService {
queryParams: { search: string; application?: string; roles?: string[] };
queryParams: { search: string; application?: string; roles?: string [] };
constructor(
private oAuth2Service: OAuth2Service,
private appConfigService: AppConfigService
) {}
constructor(private oAuth2Service: OAuth2Service, private appConfigService: AppConfigService) {}
public search(name: string, filters?: IdentityGroupFilterInterface): Observable<IdentityGroupModel[]> {
if (name.trim() === '') {
@@ -52,13 +51,13 @@ export class IdentityGroupService implements IdentityGroupServiceInterface {
return this.invokeIdentityGroupApi();
}
private searchGroupsWithGlobalRoles(name: string, roles: string []): Observable<IdentityGroupModel[]> {
private searchGroupsWithGlobalRoles(name: string, roles: string[]): Observable<IdentityGroupModel[]> {
this.buildQueryParam(name, roles);
return this.invokeIdentityGroupApi();
}
private searchGroupsWithinApp(name: string, applicationName: string, roles?: string []): Observable<IdentityGroupModel[]> {
private searchGroupsWithinApp(name: string, applicationName: string, roles?: string[]): Observable<IdentityGroupModel[]> {
this.buildQueryParam(name, roles, applicationName);
return this.invokeIdentityGroupApi();
@@ -69,13 +68,13 @@ export class IdentityGroupService implements IdentityGroupServiceInterface {
return this.oAuth2Service.get({ url, queryParams: this.queryParams });
}
private buildQueryParam(name: string, roles?: string [], applicationName?: string) {
private buildQueryParam(name: string, roles?: string[], applicationName?: string) {
this.queryParams = { search: name };
this.addOptionalValueToQueryParam('application', applicationName);
this.addOptionalCommaValueToQueryParam('role', roles);
}
private addOptionalCommaValueToQueryParam(key: string, values: string []) {
private addOptionalCommaValueToQueryParam(key: string, values: string[]) {
if (values?.length > 0) {
const valuesNotEmpty = this.filterOutEmptyValue(values);
if (valuesNotEmpty?.length > 0) {
@@ -90,8 +89,8 @@ export class IdentityGroupService implements IdentityGroupServiceInterface {
}
}
private filterOutEmptyValue(roles: string []): string [] {
return roles.filter( role => role.trim() ? true : false);
private filterOutEmptyValue(roles: string[]): string[] {
return roles.filter((role) => (role.trim() ? true : false));
}
private get identityHost(): string {

View File

@@ -15,7 +15,7 @@
* limitations under the License.
*/
import { TaskDetailsCloudModel } from '../task/start-task/models/task-details-cloud.model';
import { TaskDetailsCloudModel } from '../task/models/task-details-cloud.model';
export interface TaskCloudEngineEvent {
eventType: string;

View File

@@ -25,7 +25,6 @@
</mat-icon>
</mat-chip-row>
<input matInput
#chipInput
[disabled]="isReadonly()"
[formControl]="searchUserCtrl"
[matAutocomplete]="auto"
@@ -63,9 +62,8 @@
</ng-template>
</mat-autocomplete>
</mat-form-field>
<mat-progress-bar
*ngIf="validationLoading"
mode="indeterminate" />
<mat-progress-bar *ngIf="validationLoading" mode="indeterminate" />
<div class="adf-error-container" *ngIf="showErrors">
<mat-error *ngIf="hasPreselectError() && !isValidationLoading()" [@transitionMessages]="subscriptAnimationState" class="adf-error">

View File

@@ -19,25 +19,23 @@ import { PeopleCloudComponent } from './people-cloud.component';
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { CoreTestingModule } from '@alfresco/adf-core';
import { ProcessServiceCloudTestingModule } from '../../testing/process-service-cloud.testing.module';
import { PeopleCloudModule } from '../people-cloud.module';
import { DebugElement, SimpleChange } from '@angular/core';
import { By } from '@angular/platform-browser';
import { of } from 'rxjs';
import { IdentityUserServiceInterface } from '../services/identity-user.service.interface';
import { IDENTITY_USER_SERVICE_TOKEN } from '../services/identity-user-service.token';
import { mockFoodUsers, mockKielbasaSausage, mockShepherdsPie, mockYorkshirePudding, mockPreselectedFoodUsers } from '../mock/people-cloud.mock';
import { HarnessLoader } from '@angular/cdk/testing';
import { TestbedHarnessEnvironment } from '@angular/cdk/testing/testbed';
import { MatChipHarness } from '@angular/material/chips/testing';
import { MatInputHarness } from '@angular/material/input/testing';
import { MatFormFieldHarness } from '@angular/material/form-field/testing';
import { IdentityUserService } from '@alfresco/adf-process-services-cloud';
describe('PeopleCloudComponent', () => {
let loader: HarnessLoader;
let component: PeopleCloudComponent;
let fixture: ComponentFixture<PeopleCloudComponent>;
let element: HTMLElement;
let identityUserService: IdentityUserServiceInterface;
let identityUserService: IdentityUserService;
let searchSpy: jasmine.Spy;
/**
@@ -83,13 +81,13 @@ describe('PeopleCloudComponent', () => {
beforeEach(() => {
TestBed.configureTestingModule({
imports: [CoreTestingModule, ProcessServiceCloudTestingModule, PeopleCloudModule]
imports: [CoreTestingModule, ProcessServiceCloudTestingModule, PeopleCloudComponent]
});
fixture = TestBed.createComponent(PeopleCloudComponent);
component = fixture.componentInstance;
element = fixture.nativeElement;
identityUserService = TestBed.inject(IDENTITY_USER_SERVICE_TOKEN);
identityUserService = fixture.debugElement.injector.get(IdentityUserService);
loader = TestbedHarnessEnvironment.loader(fixture);
});

View File

@@ -17,7 +17,6 @@
import { applicationConfig, Meta, moduleMetadata, StoryFn } from '@storybook/angular';
import { PeopleCloudComponent } from './people-cloud.component';
import { PeopleCloudModule } from '../people-cloud.module';
import { ProcessServicesCloudStoryModule } from '../../testing/process-services-cloud-story.module';
import { IdentityUserService } from '../services/identity-user.service';
import { IdentityUserServiceMock, mockFoodUsers, mockKielbasaSausage, mockShepherdsPie, mockYorkshirePudding } from '../mock/people-cloud.mock';
@@ -28,13 +27,10 @@ export default {
title: 'Process Services Cloud/People Cloud/People Cloud',
decorators: [
moduleMetadata({
imports: [PeopleCloudModule]
imports: [PeopleCloudComponent]
}),
applicationConfig({
providers: [
{ provide: IdentityUserService, useClass: IdentityUserServiceMock },
importProvidersFrom(ProcessServicesCloudStoryModule)
]
providers: [{ provide: IdentityUserService, useClass: IdentityUserServiceMock }, importProvidersFrom(ProcessServicesCloudStoryModule)]
})
],
argTypes: {
@@ -119,7 +115,7 @@ export default {
description: 'FormControl to list of users.',
table: {
type: { summary: 'FormControl' },
defaultValue: { summary: 'new FormControl({ value: \'\', disabled: false })' },
defaultValue: { summary: "new FormControl({ value: '', disabled: false })" },
category: 'Form Controls'
}
},
@@ -128,7 +124,7 @@ export default {
description: 'FormControl to search the user.',
table: {
type: { summary: 'FormControl' },
defaultValue: { summary: 'new FormControl({ value: \'\', disabled: false })' },
defaultValue: { summary: "new FormControl({ value: '', disabled: false })" },
category: 'Form Controls'
}
},
@@ -173,7 +169,7 @@ export default {
}
} as Meta<PeopleCloudComponent>;
const template: StoryFn<PeopleCloudComponent> = args => ({
const template: StoryFn<PeopleCloudComponent> = (args) => ({
props: args
});

View File

@@ -15,14 +15,13 @@
* limitations under the License.
*/
import { UntypedFormControl } from '@angular/forms';
import { ReactiveFormsModule, UntypedFormControl } from '@angular/forms';
import {
AfterViewInit,
Component,
DestroyRef,
ElementRef,
EventEmitter,
Inject,
inject,
Input,
OnChanges,
@@ -34,17 +33,40 @@ import {
} from '@angular/core';
import { BehaviorSubject, Observable } from 'rxjs';
import { debounceTime, distinctUntilChanged, filter, mergeMap, switchMap, tap } from 'rxjs/operators';
import { FullNamePipe } from '@alfresco/adf-core';
import { FullNamePipe, InitialUsernamePipe } from '@alfresco/adf-core';
import { animate, state, style, transition, trigger } from '@angular/animations';
import { ComponentSelectionMode } from '../../types';
import { IdentityUserModel } from '../models/identity-user.model';
import { IdentityUserServiceInterface } from '../services/identity-user.service.interface';
import { IDENTITY_USER_SERVICE_TOKEN } from '../services/identity-user-service.token';
import { MatFormFieldAppearance, SubscriptSizing } from '@angular/material/form-field';
import { MatFormFieldAppearance, MatFormFieldModule, SubscriptSizing } from '@angular/material/form-field';
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
import { CommonModule } from '@angular/common';
import { TranslateModule } from '@ngx-translate/core';
import { MatIconModule } from '@angular/material/icon';
import { MatProgressBarModule } from '@angular/material/progress-bar';
import { MatSelectModule } from '@angular/material/select';
import { MatAutocompleteModule } from '@angular/material/autocomplete';
import { MatChipsModule } from '@angular/material/chips';
import { MatInputModule } from '@angular/material/input';
import { IdentityUserService } from '../services/identity-user.service';
@Component({
selector: 'adf-cloud-people',
standalone: true,
imports: [
CommonModule,
TranslateModule,
MatIconModule,
MatFormFieldModule,
MatProgressBarModule,
MatSelectModule,
InitialUsernamePipe,
FullNamePipe,
MatAutocompleteModule,
ReactiveFormsModule,
MatChipsModule,
MatInputModule
],
providers: [FullNamePipe],
templateUrl: './people-cloud.component.html',
styleUrls: ['./people-cloud.component.scss'],
animations: [
@@ -53,7 +75,6 @@ import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
transition('void => enter', [style({ opacity: 0, transform: 'translateY(-100%)' }), animate('300ms cubic-bezier(0.55, 0, 0.55, 0.2)')])
])
],
providers: [FullNamePipe],
encapsulation: ViewEncapsulation.None
})
export class PeopleCloudComponent implements OnInit, OnChanges, AfterViewInit {
@@ -196,10 +217,7 @@ export class PeopleCloudComponent implements OnInit, OnChanges, AfterViewInit {
private readonly destroyRef = inject(DestroyRef);
constructor(
@Inject(IDENTITY_USER_SERVICE_TOKEN)
private identityUserService: IdentityUserServiceInterface
) {}
constructor(private identityUserService: IdentityUserService) {}
ngOnInit(): void {
this.initSearch();

View File

@@ -1,72 +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 { HttpErrorResponse } from '@angular/common/http';
import { IdentityUserFilterInterface } from '../services/identity-user-filter.interface';
export const mockSearchUserEmptyFilters: IdentityUserFilterInterface = {
roles: [],
groups: [],
withinApplication: ''
};
export const mockSearchUserByGroups: IdentityUserFilterInterface = {
roles: [],
groups: ['fake-group-1', 'fake-group-2'],
withinApplication: ''
};
export const mockSearchUserByGroupsAndRoles: IdentityUserFilterInterface = {
roles: ['fake-role-1', 'fake-role-2'],
groups: ['fake-group-1', 'fake-group-2'],
withinApplication: ''
};
export const mockSearchUserByGroupsAndRolesAndApp: IdentityUserFilterInterface = {
roles: ['fake-role-1', 'fake-role-2'],
groups: ['fake-group-1', 'fake-group-2'],
withinApplication: 'fake-app-name'
};
export const mockSearchUserByRoles: IdentityUserFilterInterface = {
roles: ['fake-role-1', 'fake-role-2'],
groups: [],
withinApplication: ''
};
export const mockSearchUserByRolesAndApp: IdentityUserFilterInterface = {
roles: ['fake-role-1', 'fake-role-2'],
groups: [],
withinApplication: 'fake-app-name'
};
export const mockSearchUserByApp: IdentityUserFilterInterface = {
roles: [],
groups: [],
withinApplication: 'fake-app-name'
};
export const mockSearchUserByAppAndGroups: IdentityUserFilterInterface = {
roles: [],
groups: ['fake-group-1', 'fake-group-2'],
withinApplication: 'fake-app-name'
};
export const mockHttpErrorResponse = new HttpErrorResponse({
error: 'Mock Error',
status: 404, statusText: 'Not Found'
});

View File

@@ -1,22 +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.
*/
export const mockToken = 'eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ' +
'zdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiZmFtaWx5X25hbWUiOiJEb2UiLCJnaXZ' +
'lbl9uYW1lIjoiSm9obiIsImFkbWluIjp0cnVlLCJlbWFpbCI6ImpvaG5Eb2VAZ21haWwuY29tIiwicHJ' +
'lZmVycmVkX3VzZXJuYW1lIjoiam9obkRvZTEiLCJqdGkiOiI2MmQ3YjA4NS1hNTJjLTRjZmEtYjA2Zi1' +
'hODFhN2IwNjRjZDIiLCJpYXQiOjE1NDM0MTA0NzcsImV4cCI6MTU0MzQxNTIxM30.pSP86kmX3keuU5E3ndaOUq2TzKdJRsuMnBdFz3Y-UEU';

View File

@@ -18,12 +18,29 @@
import { Injectable } from '@angular/core';
import { Observable, EMPTY, of } from 'rxjs';
import { IdentityUserModel } from '../models/identity-user.model';
import { IdentityUserFilterInterface } from '../services/identity-user-filter.interface';
import { IdentityUserServiceInterface } from '../services/identity-user.service.interface';
import { IdentityUserService } from '@alfresco/adf-process-services-cloud';
export const mockYorkshirePudding: IdentityUserModel = { id: 'yorkshire', username: 'Yorkshire Pudding', firstName: 'Yorkshire', lastName: 'Pudding', email: 'pudding@food.com' };
export const mockShepherdsPie: IdentityUserModel = { id: 'shepherds', username: 'Shepherds Pie', firstName: 'Shepherds', lastName: 'Pie', email: 'shepherds@food.com'};
export const mockKielbasaSausage: IdentityUserModel = { id: 'kielbasa', username: 'Kielbasa Sausage', firstName: 'Kielbasa', lastName: 'Sausage', email: 'sausage@food.com' };
export const mockYorkshirePudding: IdentityUserModel = {
id: 'yorkshire',
username: 'Yorkshire Pudding',
firstName: 'Yorkshire',
lastName: 'Pudding',
email: 'pudding@food.com'
};
export const mockShepherdsPie: IdentityUserModel = {
id: 'shepherds',
username: 'Shepherds Pie',
firstName: 'Shepherds',
lastName: 'Pie',
email: 'shepherds@food.com'
};
export const mockKielbasaSausage: IdentityUserModel = {
id: 'kielbasa',
username: 'Kielbasa Sausage',
firstName: 'Kielbasa',
lastName: 'Sausage',
email: 'sausage@food.com'
};
export const mockFoodUsers: IdentityUserModel[] = [mockYorkshirePudding, mockShepherdsPie, mockKielbasaSausage];
@@ -32,24 +49,19 @@ export const mockPreselectedFoodUsers = [
{ ...mockKielbasaSausage, readonly: false }
];
@Injectable({
providedIn: 'root'
})
export class IdentityUserServiceMock implements IdentityUserServiceInterface {
@Injectable()
export class IdentityUserServiceMock extends IdentityUserService {
queryParams: { search: string; application?: string; roles?: string[]; groups?: string[] };
getCurrentUserInfo(): IdentityUserModel {
return mockKielbasaSausage;
}
search(name: string, _filters?: IdentityUserFilterInterface): Observable<IdentityUserModel[]> {
search(name: string): Observable<IdentityUserModel[]> {
if (name.trim() === '') {
return EMPTY;
}
return of(mockFoodUsers.filter(group =>
group.username.toUpperCase().includes(name.toUpperCase())
));
return of(mockFoodUsers.filter((group) => group.username.toUpperCase().includes(name.toUpperCase())));
}
}

View File

@@ -17,18 +17,10 @@
import { NgModule } from '@angular/core';
import { PeopleCloudComponent } from './components/people-cloud.component';
import { CommonModule } from '@angular/common';
import { MaterialModule } from '../material.module';
import { CoreModule, FullNamePipe, InitialUsernamePipe } from '@alfresco/adf-core';
import { FormsModule, ReactiveFormsModule } from '@angular/forms';
import { IdentityUserService } from './services/identity-user.service';
import { IDENTITY_USER_SERVICE_TOKEN } from './services/identity-user-service.token';
import { MatProgressBarModule } from '@angular/material/progress-bar';
/** @deprecated use PeopleCloudComponent instead */
@NgModule({
imports: [CommonModule, MaterialModule, FormsModule, ReactiveFormsModule, CoreModule, FullNamePipe, InitialUsernamePipe, MatProgressBarModule],
declarations: [PeopleCloudComponent],
exports: [PeopleCloudComponent],
providers: [{ provide: IDENTITY_USER_SERVICE_TOKEN, useExisting: IdentityUserService }]
imports: [PeopleCloudComponent],
exports: [PeopleCloudComponent]
})
export class PeopleCloudModule {}

View File

@@ -19,4 +19,3 @@ export * from './components/people-cloud.component';
export * from './people-cloud.module';
export * from './models/identity-user.model';
export * from './services/identity-user.service';
export * from './services/identity-user-service.token';

View File

@@ -1,22 +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.
*/
export interface IdentityUserFilterInterface {
roles?: string[];
withinApplication?: string;
groups?: string[];
}

View File

@@ -1,21 +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 { InjectionToken } from '@angular/core';
import { IdentityUserServiceInterface } from './identity-user.service.interface';
export const IDENTITY_USER_SERVICE_TOKEN = new InjectionToken<IdentityUserServiceInterface>('identity-user-service-token');

View File

@@ -1,25 +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 { Observable } from 'rxjs';
import { IdentityUserModel } from '../models/identity-user.model';
import { IdentityUserFilterInterface } from './identity-user-filter.interface';
export interface IdentityUserServiceInterface {
getCurrentUserInfo(): IdentityUserModel;
search(name: string, filters?: IdentityUserFilterInterface): Observable<IdentityUserModel[]>;
}

View File

@@ -18,18 +18,15 @@
import { TestBed } from '@angular/core/testing';
import { IdentityUserService } from './identity-user.service';
import { ProcessServiceCloudTestingModule } from '../../testing/process-service-cloud.testing.module';
import {
mockSearchUserByApp,
mockSearchUserByAppAndGroups,
mockSearchUserByGroups,
mockSearchUserByGroupsAndRoles,
mockSearchUserByGroupsAndRolesAndApp,
mockSearchUserByRoles,
mockSearchUserByRolesAndApp
} from '../mock/identity-user.service.mock';
import { mockFoodUsers } from '../mock/people-cloud.mock';
import { AdfHttpClient } from '@alfresco/adf-core/api';
import { mockHttpErrorResponse } from '../../group/mock/identity-group.service.mock';
import { HttpErrorResponse } from '@angular/common/http';
const mockHttpErrorResponse = new HttpErrorResponse({
error: 'Mock Error',
status: 404,
statusText: 'Not Found'
});
describe('IdentityUserService', () => {
let service: IdentityUserService;
@@ -83,154 +80,214 @@ describe('IdentityUserService', () => {
requestSpy.and.returnValue(Promise.resolve(mockFoodUsers));
const searchSpy = spyOn(service, 'search').and.callThrough();
service.search('fake', mockSearchUserByRoles).subscribe((res) => {
expect(res).toBeDefined();
expect(searchSpy).toHaveBeenCalled();
expect(service.queryParams).toEqual({
search: 'fake',
role: 'fake-role-1,fake-role-2'
service
.search('fake', {
roles: ['fake-role-1', 'fake-role-2'],
groups: [],
withinApplication: ''
})
.subscribe((res) => {
expect(res).toBeDefined();
expect(searchSpy).toHaveBeenCalled();
expect(service.queryParams).toEqual({
search: 'fake',
role: 'fake-role-1,fake-role-2'
});
done();
});
done();
});
});
it('should not fetch users by roles if error occurred', (done) => {
requestSpy.and.returnValue(Promise.reject(mockHttpErrorResponse));
service.search('fake', mockSearchUserByRoles).subscribe(
() => {
fail('expected an error, not users');
},
(error) => {
expect(error.status).toEqual(404);
expect(error.statusText).toEqual('Not Found');
expect(error.error).toEqual('Mock Error');
done();
}
);
service
.search('fake', {
roles: ['fake-role-1', 'fake-role-2'],
groups: [],
withinApplication: ''
})
.subscribe(
() => {
fail('expected an error, not users');
},
(error) => {
expect(error.status).toEqual(404);
expect(error.statusText).toEqual('Not Found');
expect(error.error).toEqual('Mock Error');
done();
}
);
});
it('should fetch users by groups', (done) => {
requestSpy.and.returnValue(Promise.resolve(mockFoodUsers));
const searchSpy = spyOn(service, 'search').and.callThrough();
service.search('fake', mockSearchUserByGroups).subscribe((res) => {
expect(res).toBeDefined();
expect(searchSpy).toHaveBeenCalled();
expect(service.queryParams).toEqual({
search: 'fake',
group: 'fake-group-1,fake-group-2'
service
.search('fake', {
roles: [],
groups: ['fake-group-1', 'fake-group-2'],
withinApplication: ''
})
.subscribe((res) => {
expect(res).toBeDefined();
expect(searchSpy).toHaveBeenCalled();
expect(service.queryParams).toEqual({
search: 'fake',
group: 'fake-group-1,fake-group-2'
});
done();
});
done();
});
});
it('should fetch users by roles with groups', (done) => {
requestSpy.and.returnValue(Promise.resolve(mockFoodUsers));
const searchSpy = spyOn(service, 'search').and.callThrough();
service.search('fake', mockSearchUserByGroupsAndRoles).subscribe((res) => {
expect(res).toBeDefined();
expect(searchSpy).toHaveBeenCalled();
expect(service.queryParams).toEqual({
search: 'fake',
role: 'fake-role-1,fake-role-2',
group: 'fake-group-1,fake-group-2'
service
.search('fake', {
roles: ['fake-role-1', 'fake-role-2'],
groups: ['fake-group-1', 'fake-group-2'],
withinApplication: ''
})
.subscribe((res) => {
expect(res).toBeDefined();
expect(searchSpy).toHaveBeenCalled();
expect(service.queryParams).toEqual({
search: 'fake',
role: 'fake-role-1,fake-role-2',
group: 'fake-group-1,fake-group-2'
});
done();
});
done();
});
});
it('should fetch users by roles with groups and appName', (done) => {
requestSpy.and.returnValue(Promise.resolve(mockFoodUsers));
const searchSpy = spyOn(service, 'search').and.callThrough();
service.search('fake', mockSearchUserByGroupsAndRolesAndApp).subscribe((res) => {
expect(res).toBeDefined();
expect(searchSpy).toHaveBeenCalled();
expect(service.queryParams).toEqual({
search: 'fake',
role: 'fake-role-1,fake-role-2',
application: 'fake-app-name',
group: 'fake-group-1,fake-group-2'
service
.search('fake', {
roles: ['fake-role-1', 'fake-role-2'],
groups: ['fake-group-1', 'fake-group-2'],
withinApplication: 'fake-app-name'
})
.subscribe((res) => {
expect(res).toBeDefined();
expect(searchSpy).toHaveBeenCalled();
expect(service.queryParams).toEqual({
search: 'fake',
role: 'fake-role-1,fake-role-2',
application: 'fake-app-name',
group: 'fake-group-1,fake-group-2'
});
done();
});
done();
});
});
it('should not fetch users by groups if error occurred', (done) => {
requestSpy.and.returnValue(Promise.reject(mockHttpErrorResponse));
service.search('fake', mockSearchUserByGroups).subscribe(
() => {
fail('expected an error, not users');
},
(error) => {
expect(error.status).toEqual(404);
expect(error.statusText).toEqual('Not Found');
expect(error.error).toEqual('Mock Error');
done();
}
);
service
.search('fake', {
roles: [],
groups: ['fake-group-1', 'fake-group-2'],
withinApplication: ''
})
.subscribe(
() => {
fail('expected an error, not users');
},
(error) => {
expect(error.status).toEqual(404);
expect(error.statusText).toEqual('Not Found');
expect(error.error).toEqual('Mock Error');
done();
}
);
});
it('should fetch users within app', (done) => {
requestSpy.and.returnValue(Promise.resolve(mockFoodUsers));
service.search('fake', mockSearchUserByApp).subscribe((res) => {
expect(res).toBeDefined();
expect(service.queryParams).toEqual({
search: 'fake',
application: 'fake-app-name'
service
.search('fake', {
roles: [],
groups: [],
withinApplication: 'fake-app-name'
})
.subscribe((res) => {
expect(res).toBeDefined();
expect(service.queryParams).toEqual({
search: 'fake',
application: 'fake-app-name'
});
done();
});
done();
});
});
it('should fetch users within app with roles', (done) => {
requestSpy.and.returnValue(Promise.resolve(mockFoodUsers));
service.search('fake', mockSearchUserByRolesAndApp).subscribe((res) => {
expect(res).toBeDefined();
expect(service.queryParams).toEqual({
search: 'fake',
application: 'fake-app-name',
role: 'fake-role-1,fake-role-2'
service
.search('fake', {
roles: ['fake-role-1', 'fake-role-2'],
groups: [],
withinApplication: 'fake-app-name'
})
.subscribe((res) => {
expect(res).toBeDefined();
expect(service.queryParams).toEqual({
search: 'fake',
application: 'fake-app-name',
role: 'fake-role-1,fake-role-2'
});
done();
});
done();
});
});
it('should fetch users within app with groups', (done) => {
requestSpy.and.returnValue(Promise.resolve(mockFoodUsers));
const searchSpy = spyOn(service, 'search').and.callThrough();
service.search('fake', mockSearchUserByAppAndGroups).subscribe((res) => {
expect(res).toBeDefined();
expect(searchSpy).toHaveBeenCalled();
expect(service.queryParams).toEqual({
search: 'fake',
application: 'fake-app-name',
group: 'fake-group-1,fake-group-2'
service
.search('fake', {
roles: [],
groups: ['fake-group-1', 'fake-group-2'],
withinApplication: 'fake-app-name'
})
.subscribe((res) => {
expect(res).toBeDefined();
expect(searchSpy).toHaveBeenCalled();
expect(service.queryParams).toEqual({
search: 'fake',
application: 'fake-app-name',
group: 'fake-group-1,fake-group-2'
});
done();
});
done();
});
});
it('should not fetch users within app if error occurred', (done) => {
requestSpy.and.returnValue(Promise.reject(mockHttpErrorResponse));
service.search('fake', mockSearchUserByApp).subscribe(
() => {
fail('expected an error, not users');
},
(error) => {
expect(error.status).toEqual(404);
expect(error.statusText).toEqual('Not Found');
expect(error.error).toEqual('Mock Error');
done();
}
);
service
.search('fake', {
roles: [],
groups: [],
withinApplication: 'fake-app-name'
})
.subscribe(
() => {
fail('expected an error, not users');
},
(error) => {
expect(error.status).toEqual(404);
expect(error.statusText).toEqual('Not Found');
expect(error.error).toEqual('Mock Error');
done();
}
);
});
});
});

View File

@@ -18,16 +18,20 @@
import { Injectable } from '@angular/core';
import { AppConfigService, JwtHelperService, OAuth2Service } from '@alfresco/adf-core';
import { EMPTY, Observable } from 'rxjs';
import { IdentityUserServiceInterface } from './identity-user.service.interface';
import { IdentityUserModel } from '../models/identity-user.model';
import { IdentityUserFilterInterface } from './identity-user-filter.interface';
const IDENTITY_MICRO_SERVICE_INGRESS = 'identity-adapter-service';
export interface IdentityUserFilterInterface {
roles?: string[];
withinApplication?: string;
groups?: string[];
}
@Injectable({
providedIn: 'root'
})
export class IdentityUserService implements IdentityUserServiceInterface {
export class IdentityUserService {
queryParams: { search: string; application?: string; roles?: string[]; groups?: string[] };
constructor(private jwtHelperService: JwtHelperService, private oAuth2Service: OAuth2Service, private appConfigService: AppConfigService) {}

View File

@@ -20,9 +20,8 @@ import { CoreModule, FormRenderingService, provideTranslations } from '@alfresco
import { APP_LIST_CLOUD_DIRECTIVES } from './app/app-list-cloud.module';
import { TaskCloudModule } from './task/task-cloud.module';
import { ProcessCloudModule } from './process/process-cloud.module';
import { GroupCloudModule } from './group/group-cloud.module';
import { FORM_CLOUD_DIRECTIVES } from './form/form-cloud.module';
import { TaskFormModule } from './task/task-form/task-form.module';
import { TASK_FORM_CLOUD_DIRECTIVES } from './task/task-form/task-form.module';
import {
LocalPreferenceCloudService,
PreferenceCloudServiceInterface,
@@ -31,10 +30,11 @@ import {
PROCESS_LISTS_PREFERENCES_SERVICE_TOKEN,
TASK_LIST_PREFERENCES_SERVICE_TOKEN
} from './services/public-api';
import { PeopleCloudModule } from './people/people-cloud.module';
import { CloudFormRenderingService } from './form/components/cloud-form-rendering.service';
import { ApolloModule } from 'apollo-angular';
import { RichTextEditorComponent } from './rich-text-editor';
import { GroupCloudComponent } from './group/components/group-cloud.component';
import { PeopleCloudComponent } from './people/components/people-cloud.component';
@NgModule({
imports: [
@@ -42,10 +42,10 @@ import { RichTextEditorComponent } from './rich-text-editor';
...APP_LIST_CLOUD_DIRECTIVES,
ProcessCloudModule,
TaskCloudModule,
GroupCloudModule,
PeopleCloudModule,
GroupCloudComponent,
PeopleCloudComponent,
...FORM_CLOUD_DIRECTIVES,
TaskFormModule,
...TASK_FORM_CLOUD_DIRECTIVES,
ApolloModule,
RichTextEditorComponent
],
@@ -54,10 +54,10 @@ import { RichTextEditorComponent } from './rich-text-editor';
...APP_LIST_CLOUD_DIRECTIVES,
ProcessCloudModule,
TaskCloudModule,
GroupCloudModule,
GroupCloudComponent,
...FORM_CLOUD_DIRECTIVES,
TaskFormModule,
PeopleCloudModule,
...TASK_FORM_CLOUD_DIRECTIVES,
PeopleCloudComponent,
RichTextEditorComponent
]
})

View File

@@ -23,12 +23,10 @@ import { ApplicationVersionModel } from '../../models/application-version.model'
import { processInstancePlaceholdersCloudMock, processInstanceDetailsCloudMock } from './process-instance-details-cloud.mock';
import { fakeProcessDefinitions } from '../start-process/mock/start-process.component.mock';
import { mockAppVersions } from '../process-filters/mock/process-filters-cloud.mock';
import { ProcessCloudInterface } from '../services/process-cloud.interface';
import { ProcessCloudService } from '@alfresco/adf-process-services-cloud';
@Injectable({
providedIn: 'root'
})
export class ProcessCloudServiceMock implements ProcessCloudInterface {
@Injectable()
export class ProcessCloudServiceMock extends ProcessCloudService {
dataChangesDetected = new Subject<ProcessInstanceCloud>();
getProcessInstanceById(appName: string, processInstanceId: string): Observable<ProcessInstanceCloud> {

View File

@@ -47,7 +47,6 @@ import { DateCloudFilterType } from '../../../models/date-cloud-filter.model';
import { ProcessDefinitionCloud } from '../../../models/process-definition-cloud.model';
import { PeopleCloudComponent } from '../../../people/components/people-cloud.component';
import { IdentityUserServiceMock } from '../../../people/mock/people-cloud.mock';
import { IDENTITY_USER_SERVICE_TOKEN } from '../../../people/services/identity-user-service.token';
import { PROCESS_FILTERS_SERVICE_TOKEN } from '../../../services/cloud-token.service';
import { LocalPreferenceCloudService } from '../../../services/local-preference-cloud.service';
import { NotificationCloudService } from '../../../services/notification-cloud.service';
@@ -62,6 +61,7 @@ import {
PROCESS_FILTER_ACTION_SAVE_DEFAULT
} from './edit-process-filter-cloud.component';
import { ProcessFilterDialogCloudComponent } from './process-filter-dialog-cloud.component';
import { IdentityUserService } from '@alfresco/adf-process-services-cloud';
describe('EditProcessFilterCloudComponent', () => {
let loader: HarnessLoader;
@@ -114,7 +114,9 @@ describe('EditProcessFilterCloudComponent', () => {
MatInputModule,
ReactiveFormsModule,
MatChipsModule,
MatProgressBarModule
MatProgressBarModule,
PeopleCloudComponent,
DateRangeFilterComponent
],
providers: [
{ provide: PROCESS_FILTERS_SERVICE_TOKEN, useClass: LocalPreferenceCloudService },
@@ -122,9 +124,8 @@ describe('EditProcessFilterCloudComponent', () => {
{ provide: DateAdapter, useClass: DateFnsAdapter },
{ provide: NotificationCloudService, useValue: { makeGQLQuery: () => of([]) } },
{ provide: MAT_DATE_FORMATS, useValue: ADF_DATE_FORMATS },
{ provide: IDENTITY_USER_SERVICE_TOKEN, useExisting: IdentityUserServiceMock }
],
declarations: [PeopleCloudComponent, DateRangeFilterComponent]
{ provide: IdentityUserService, useClass: IdentityUserServiceMock }
]
});
fixture = TestBed.createComponent(EditProcessFilterCloudComponent);
component = fixture.componentInstance;

View File

@@ -25,8 +25,8 @@ import { HttpClientModule } from '@angular/common/http';
import { EditProcessFilterCloudComponent } from './components/edit-process-filter-cloud.component';
import { ProcessFilterDialogCloudComponent } from './components/process-filter-dialog-cloud.component';
import { APP_LIST_CLOUD_DIRECTIVES } from './../../app/app-list-cloud.module';
import { ProcessCommonModule } from '../../common/process-common.module';
import { PeopleCloudModule } from '../../people/people-cloud.module';
import { PeopleCloudComponent } from '../../people/components/people-cloud.component';
import { DateRangeFilterComponent } from '../../common/date-range-filter/date-range-filter.component';
@NgModule({
imports: [
@@ -37,8 +37,8 @@ import { PeopleCloudModule } from '../../people/people-cloud.module';
MaterialModule,
...APP_LIST_CLOUD_DIRECTIVES,
CoreModule,
ProcessCommonModule,
PeopleCloudModule
DateRangeFilterComponent,
PeopleCloudComponent
],
declarations: [ProcessFiltersCloudComponent, EditProcessFilterCloudComponent, ProcessFilterDialogCloudComponent],
exports: [ProcessFiltersCloudComponent, EditProcessFilterCloudComponent, ProcessFilterDialogCloudComponent]

View File

@@ -1,31 +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 { Observable, Subject } from 'rxjs';
import { ProcessInstanceCloud } from '../start-process/models/process-instance-cloud.model';
import { ProcessDefinitionCloud } from '../../models/process-definition-cloud.model';
import { ApplicationVersionModel } from '../../models/application-version.model';
export interface ProcessCloudInterface {
dataChangesDetected: Subject<ProcessInstanceCloud>;
getProcessInstanceById(appName: string, processInstanceId: string): Observable<ProcessInstanceCloud>;
getProcessDefinitions(appName: string): Observable<ProcessDefinitionCloud[]>;
getApplicationVersions(appName: string): Observable<ApplicationVersionModel[]>;
cancelProcess(appName: string, processInstanceId: string): Observable<ProcessInstanceCloud>;
}

View File

@@ -22,12 +22,11 @@ import { ProcessInstanceCloud } from '../start-process/models/process-instance-c
import { BaseCloudService } from '../../services/base-cloud.service';
import { ProcessDefinitionCloud } from '../../models/process-definition-cloud.model';
import { ApplicationVersionModel, ApplicationVersionResponseModel } from '../../models/application-version.model';
import { ProcessCloudInterface } from './process-cloud.interface';
@Injectable({
providedIn: 'root'
})
export class ProcessCloudService extends BaseCloudService implements ProcessCloudInterface {
export class ProcessCloudService extends BaseCloudService {
dataChangesDetected = new Subject<ProcessInstanceCloud>();
/**

View File

@@ -1,22 +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.
*/
export * from './claim-task-cloud.directive';
export * from './unclaim-task-cloud.directive';
export * from './complete-task.directive';
export * from './task-directive.module';

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 { CompleteTaskDirective } from './complete-task.directive';
import { ClaimTaskCloudDirective } from './claim-task-cloud.directive';
import { UnClaimTaskCloudDirective } from './unclaim-task-cloud.directive';
export const TASK_DIRECTIVES = [CompleteTaskDirective, ClaimTaskCloudDirective, UnClaimTaskCloudDirective] as const;
/** @deprecated */
@NgModule({
imports: [...TASK_DIRECTIVES],
exports: [...TASK_DIRECTIVES]
})
export class TaskDirectiveModule {}

View File

@@ -16,23 +16,23 @@
*/
import { Injectable } from '@angular/core';
import { AppConfigService, CardViewArrayItem } from '@alfresco/adf-core';
import { CardViewArrayItem } from '@alfresco/adf-core';
import { from, Observable, of, Subject, throwError } from 'rxjs';
import { DEFAULT_TASK_PRIORITIES, TaskPriorityOption } from '../models/task.model';
import { TaskDetailsCloudModel, TASK_ASSIGNED_STATE, TASK_CREATED_STATE } from '../start-task/models/task-details-cloud.model';
import { TaskDetailsCloudModel, TASK_ASSIGNED_STATE, TASK_CREATED_STATE } from '../models/task-details-cloud.model';
import { taskDetailsContainer } from '../task-header/mocks/task-details-cloud.mock';
import { ProcessDefinitionCloud } from '../../models/process-definition-cloud.model';
import { StartTaskCloudRequestModel } from '../start-task/models/start-task-cloud-request.model';
import { TaskCloudServiceInterface } from '../services/task-cloud.service.interface';
import { TaskCloudService } from '@alfresco/adf-process-services-cloud';
import { AdfHttpClient } from '@alfresco/adf-core/api';
@Injectable({
providedIn: 'root'
})
export class TaskCloudServiceMock implements TaskCloudServiceInterface {
@Injectable()
export class TaskCloudServiceMock extends TaskCloudService {
currentUserMock = 'AssignedTaskUser';
dataChangesDetected$ = new Subject();
constructor(private appConfigService: AppConfigService) {}
constructor(adfHttpClient: AdfHttpClient) {
super(adfHttpClient);
}
getTaskById(_appName: string, taskId: string): Observable<TaskDetailsCloudModel> {
return of(taskDetailsContainer[taskId]);
@@ -80,7 +80,7 @@ export class TaskCloudServiceMock implements TaskCloudServiceInterface {
return isClickable;
}
updateTask(_appName: string, taskId: string, _payload: any): Observable<TaskDetailsCloudModel> {
updateTask(_appName: string, taskId: string): Observable<TaskDetailsCloudModel> {
return of(taskDetailsContainer[taskId]);
}
@@ -92,12 +92,8 @@ export class TaskCloudServiceMock implements TaskCloudServiceInterface {
return taskDetails && taskDetails.status === TASK_CREATED_STATE;
}
private isAssignedToMe(assignee: string): boolean {
if (assignee === this.currentUserMock) {
return true;
}
return false;
protected isAssignedToMe(assignee: string): boolean {
return assignee === this.currentUserMock;
}
completeTask(appName: string, taskId: string): Observable<TaskDetailsCloudModel> {
@@ -106,7 +102,7 @@ export class TaskCloudServiceMock implements TaskCloudServiceInterface {
return from([]);
} else {
return throwError('AppName/TaskId not configured');
return throwError(() => new Error('AppName/TaskId not configured'));
}
}
@@ -115,13 +111,13 @@ export class TaskCloudServiceMock implements TaskCloudServiceInterface {
return taskDetails && taskDetails.status === TASK_ASSIGNED_STATE && taskDetails.assignee === currentUser;
}
claimTask(appName: string, taskId: string, _assignee: string): Observable<TaskDetailsCloudModel> {
claimTask(appName: string, taskId: string): Observable<TaskDetailsCloudModel> {
if ((appName || appName === '') && taskId) {
window.alert('Claim task mock');
return from([]);
} else {
return throwError('AppName/TaskId not configured');
return throwError(() => new Error('AppName/TaskId not configured'));
}
}
@@ -131,11 +127,11 @@ export class TaskCloudServiceMock implements TaskCloudServiceInterface {
return from([]);
} else {
return throwError('AppName/TaskId not configured');
return throwError(() => new Error('AppName/TaskId not configured'));
}
}
createNewTask(_startTaskRequest: StartTaskCloudRequestModel, _appName: string): Observable<TaskDetailsCloudModel> {
createNewTask(): Observable<TaskDetailsCloudModel> {
window.alert('Create new task mock');
return from([]);
@@ -147,17 +143,17 @@ export class TaskCloudServiceMock implements TaskCloudServiceInterface {
return from([]);
} else {
return throwError('AppName not configured');
return throwError(() => new Error('AppName not configured'));
}
}
assign(appName: string, taskId: string, _assignee: string): Observable<TaskDetailsCloudModel> {
assign(appName: string, taskId: string): Observable<TaskDetailsCloudModel> {
if (appName && taskId) {
window.alert('Assign mock');
return from([]);
} else {
return throwError('AppName/TaskId not configured');
return throwError(() => new Error('AppName/TaskId not configured'));
}
}
}

View File

@@ -1,18 +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.
*/
export * from './task.model';

View File

@@ -16,7 +16,6 @@
*/
export class StartTaskCloudRequestModel {
name: string;
description: string;
assignee: string;

View File

@@ -51,12 +51,7 @@ export interface StartTaskCloudResponseModel {
entry: TaskDetailsCloudModel;
}
export type TaskStatus =
'COMPLETED' |
'CREATED' |
'ASSIGNED' |
'SUSPENDED' |
'CANCELLED';
export type TaskStatus = 'COMPLETED' | 'CREATED' | 'ASSIGNED' | 'SUSPENDED' | 'CANCELLED';
export const TASK_COMPLETED_STATE: TaskStatus = 'COMPLETED';
export const TASK_CREATED_STATE: TaskStatus = 'CREATED';
@@ -64,11 +59,7 @@ export const TASK_ASSIGNED_STATE: TaskStatus = 'ASSIGNED';
export const TASK_SUSPENDED_STATE: TaskStatus = 'SUSPENDED';
export const TASK_CANCELLED_STATE: TaskStatus = 'CANCELLED';
export type TaskPermissions =
'VIEW' |
'CLAIM' |
'RELEASE' |
'UPDATE';
export type TaskPermissions = 'VIEW' | 'CLAIM' | 'RELEASE' | 'UPDATE';
export const TASK_CLAIM_PERMISSION: TaskPermissions = 'CLAIM';
export const TASK_RELEASE_PERMISSION: TaskPermissions = 'RELEASE';

View File

@@ -17,12 +17,10 @@
export * from './task-list/public-api';
export * from './task-filters/public-api';
export * from './start-task/public-api';
export * from './models/task-details-cloud.model';
export * from './models/task.model';
export * from './models/start-task-cloud-request.model';
export * from './task-header/public-api';
export * from './task-form/public-api';
export * from './directives/public-api';
export * from './models/public-api';
export * from './services/task-cloud.service';
export * from './task-cloud.module';

View File

@@ -1,45 +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 { CardViewArrayItem } from '@alfresco/adf-core';
import { Observable, Subject } from 'rxjs';
import { ProcessDefinitionCloud } from '../../models/process-definition-cloud.model';
import { TaskPriorityOption } from '../models/task.model';
import { StartTaskCloudRequestModel } from '../start-task/models/start-task-cloud-request.model';
import { TaskDetailsCloudModel } from '../start-task/models/task-details-cloud.model';
export interface TaskCloudServiceInterface {
dataChangesDetected$: Subject<unknown>;
priorities: TaskPriorityOption[];
completeTask(appName: string, taskId: string): Observable<TaskDetailsCloudModel>;
canCompleteTask(taskDetails: TaskDetailsCloudModel): boolean;
isTaskEditable(taskDetails: TaskDetailsCloudModel): boolean;
isAssigneePropertyClickable(taskDetails: TaskDetailsCloudModel, candidateUsers: CardViewArrayItem[], candidateGroups: CardViewArrayItem[]): boolean;
canClaimTask(taskDetails: TaskDetailsCloudModel): boolean;
canUnclaimTask(taskDetails: TaskDetailsCloudModel): boolean;
claimTask(appName: string, taskId: string, assignee: string): Observable<TaskDetailsCloudModel>;
unclaimTask(appName: string, taskId: string): Observable<TaskDetailsCloudModel>;
getTaskById(appName: string, taskId: string): Observable<TaskDetailsCloudModel>;
createNewTask(startTaskRequest: StartTaskCloudRequestModel, appName: string): Observable<TaskDetailsCloudModel>;
updateTask(appName: string, taskId: string, payload: any): Observable<TaskDetailsCloudModel>;
getCandidateUsers(appName: string, taskId: string): Observable<string[]>;
getCandidateGroups(appName: string, taskId: string): Observable<string[]>;
getProcessDefinitions(appName: string): Observable<ProcessDefinitionCloud[]>;
assign(appName: string, taskId: string, assignee: string): Observable<TaskDetailsCloudModel>;
getPriorityLabel(priority: number): string;
}

View File

@@ -15,7 +15,7 @@
* limitations under the License.
*/
import { Injectable } from '@angular/core';
import { inject, Injectable } from '@angular/core';
import { CardViewArrayItem, TranslationService } from '@alfresco/adf-core';
import { throwError, Observable, of, Subject } from 'rxjs';
import { map } from 'rxjs/operators';
@@ -26,22 +26,24 @@ import {
TASK_CLAIM_PERMISSION,
TASK_CREATED_STATE,
TASK_RELEASE_PERMISSION
} from '../start-task/models/task-details-cloud.model';
} from '../models/task-details-cloud.model';
import { BaseCloudService } from '../../services/base-cloud.service';
import { StartTaskCloudRequestModel } from '../start-task/models/start-task-cloud-request.model';
import { StartTaskCloudRequestModel } from '../models/start-task-cloud-request.model';
import { ProcessDefinitionCloud } from '../../models/process-definition-cloud.model';
import { DEFAULT_TASK_PRIORITIES, TaskPriorityOption } from '../models/task.model';
import { TaskCloudServiceInterface } from './task-cloud.service.interface';
import { IdentityUserService } from '../../people/services/identity-user.service';
import { AdfHttpClient } from '@alfresco/adf-core/api';
@Injectable({
providedIn: 'root'
})
export class TaskCloudService extends BaseCloudService implements TaskCloudServiceInterface {
export class TaskCloudService extends BaseCloudService {
private translateService = inject(TranslationService);
private identityUserService = inject(IdentityUserService);
dataChangesDetected$ = new Subject();
constructor(private translateService: TranslationService, private identityUserService: IdentityUserService, adfHttpClient: AdfHttpClient) {
constructor(adfHttpClient: AdfHttpClient) {
super(adfHttpClient);
}
@@ -293,7 +295,7 @@ export class TaskCloudService extends BaseCloudService implements TaskCloudServi
return this.appConfigService.get('adf-cloud-priority-values') || DEFAULT_TASK_PRIORITIES;
}
private isAssignedToMe(assignee: string): boolean {
protected isAssignedToMe(assignee: string): boolean {
const currentUser = this.identityUserService.getCurrentUserInfo().username;
return assignee === currentUser;
}

View File

@@ -1,18 +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.
*/
export * from './models/task-details-cloud.model';

View File

@@ -17,13 +17,12 @@
import { NgModule } from '@angular/core';
import { TaskListCloudModule } from './task-list/task-list-cloud.module';
import { TaskFiltersCloudModule } from './task-filters/task-filters-cloud.module';
import { TASK_DIRECTIVES } from './directives/task-directive.module';
import { TaskFormModule } from './task-form/task-form.module';
import { TASK_FILTERS_CLOUD_DIRECTIVES } from './task-filters/task-filters-cloud.module';
import { TASK_FORM_CLOUD_DIRECTIVES } from './task-form/task-form.module';
import { TaskHeaderCloudComponent } from './task-header/components/task-header-cloud.component';
@NgModule({
imports: [TaskListCloudModule, TaskFiltersCloudModule, TaskHeaderCloudComponent, ...TASK_DIRECTIVES, TaskFormModule],
exports: [TaskListCloudModule, TaskFiltersCloudModule, TaskHeaderCloudComponent, ...TASK_DIRECTIVES, TaskFormModule]
imports: [TaskListCloudModule, ...TASK_FILTERS_CLOUD_DIRECTIVES, TaskHeaderCloudComponent, ...TASK_FORM_CLOUD_DIRECTIVES],
exports: [TaskListCloudModule, ...TASK_FILTERS_CLOUD_DIRECTIVES, TaskHeaderCloudComponent, ...TASK_FORM_CLOUD_DIRECTIVES]
})
export class TaskCloudModule {}

View File

@@ -0,0 +1,117 @@
<mat-accordion [hideToggle]="isLoading" class="adf-edit-task-filter">
<mat-expansion-panel (afterExpand)="onExpand()" (closed)="onClose()">
<mat-expansion-panel-header *ngIf="taskFilter" id="adf-edit-task-filter-expansion-header" class="adf-edit-task-filter-header">
<ng-container *ngIf="!isLoading; else loadingTemplate">
<mat-panel-title *ngIf="showTaskFilterName" id="adf-edit-task-filter-title-id" class="adf-edit-task-filter-header__title"
>{{taskFilter.name | translate}}</mat-panel-title>
<mat-panel-description class="adf-edit-task-filter-header__description" id="adf-edit-task-filter-sub-title-id">
<span *ngIf="showTitle">{{ 'ADF_CLOUD_EDIT_TASK_FILTER.TITLE' | translate}}</span>
<div *ngIf="showFilterActions" class="adf-cloud-edit-task-filter-actions">
<ng-container *ngIf="toggleFilterActions">
<button *ngFor="let filterAction of taskFilterActions"
mat-icon-button
[title]="filterAction.tooltip | translate"
[attr.data-automation-id]="'adf-filter-action-' + filterAction.actionType"
[disabled]="isDisabledAction(filterAction)"
(click)="executeFilterActions(filterAction)">
<adf-icon [value]="filterAction.icon" />
</button>
</ng-container>
</div>
</mat-panel-description>
</ng-container>
<ng-template #loadingTemplate>
<div class="adf-cloud-edit-task-filter-loading-margin">
<mat-progress-spinner mode="indeterminate" [diameter]="30" />
</div>
</ng-template>
</mat-expansion-panel-header>
<ng-container *ngIf="!isLoading;">
<form *ngIf="editTaskFilterForm" [formGroup]="editTaskFilterForm" class="adf-edit-task-filter-content">
<div class="adf-edit-task-filter-form">
<ng-container *ngFor="let taskFilterProperty of taskFilterProperties">
<mat-form-field [floatLabel]="'auto'"
*ngIf="taskFilterProperty.type === 'select'"
[attr.data-automation-id]="taskFilterProperty.key">
<mat-label class="adf-edit-task-filter-content__select-label">{{taskFilterProperty.label | translate}}</mat-label>
<mat-select
[formControlName]="taskFilterProperty.key"
[attr.data-automation-id]="'adf-cloud-edit-task-property-' + taskFilterProperty.key"
(selectionChange)="onStatusChange($event)">
<mat-option *ngFor="let propertyOption of taskFilterProperty.options"
[value]="propertyOption.value"
[attr.data-automation-id]="'adf-cloud-edit-task-property-options-' + taskFilterProperty.key">
{{ propertyOption.label | translate }}
</mat-option>
</mat-select>
</mat-form-field>
<mat-form-field [floatLabel]="'auto'"
*ngIf="taskFilterProperty.type === 'text'"
[attr.data-automation-id]="taskFilterProperty.key">
<mat-label class="adf-edit-task-filter-content__text-label">{{taskFilterProperty.label | translate}}</mat-label>
<input matInput
[formControlName]="taskFilterProperty.key"
type="text"
[attr.data-automation-id]="'adf-cloud-edit-task-property-' + taskFilterProperty.key" />
</mat-form-field>
<mat-form-field [floatLabel]="'auto'"
*ngIf="taskFilterProperty.type === 'date'"
[attr.data-automation-id]="taskFilterProperty.key">
<mat-label>{{taskFilterProperty.label | translate}}</mat-label>
<input matInput
(keyup)="onDateChanged($any($event).target.value, taskFilterProperty)"
(dateChange)="onDateChanged($event.value, taskFilterProperty)"
[matDatepicker]="dateController"
[placeholder]="taskFilterProperty.label | translate"
[attr.data-automation-id]="'adf-cloud-edit-task-property-' + taskFilterProperty.key">
<mat-datepicker-toggle matSuffix
[for]="dateController"
[attr.data-automation-id]="'adf-cloud-edit-task-property-date-toggle-' + taskFilterProperty.key" />
<mat-datepicker #dateController
[attr.data-automation-id]="'adf-cloud-edit-task-property-date-picker-' + taskFilterProperty.key" />
<div class="adf-edit-task-filter-date-error-container">
<div *ngIf="hasError(taskFilterProperty)">
<div class="adf-error-text">{{'ADF_TASK_LIST.START_TASK.FORM.ERROR.DATE'|translate}}</div>
<mat-icon class="adf-error-icon">warning</mat-icon>
</div>
</div>
</mat-form-field>
<div class="adf-edit-task-filter-checkbox"
*ngIf="taskFilterProperty.type === 'checkbox'">
<mat-checkbox color="primary"
[formControlName]="taskFilterProperty.key"
[attr.data-automation-id]="taskFilterProperty.key"
>{{taskFilterProperty.label | translate}}</mat-checkbox>
</div>
<adf-cloud-date-range-filter
*ngIf="taskFilterProperty.type === 'date-range'"
[processFilterProperty]="taskFilterProperty"
[options]="taskFilterProperty.dateFilterOptions"
(dateTypeChange)="onDateTypeChange($event, taskFilterProperty)"
(dateChanged)="onDateRangeFilterChanged($event, taskFilterProperty)" />
<adf-cloud-people
class="{{ 'adf-edit-task-filter-' + taskFilterProperty.key }}"
*ngIf="taskFilterProperty.type === 'people'"
[preSelectUsers]="taskFilterProperty.value"
[title]="taskFilterProperty.label"
[validate]="true"
[appName]="appName"
[mode]="taskFilterProperty.selectionMode"
(changedUsers)="onChangedUser($event, taskFilterProperty)" />
<adf-cloud-task-assignment-filter
*ngIf="taskFilterProperty.type === 'assignment'"
[taskFilterProperty]="taskFilterProperty"
[status]="selectedStatus"
[appName]="appName"
(assignedUsersChange)="onAssignedUsersChange($event)"
(assignedGroupsChange)="onAssignedGroupsChange($event)"
(assignmentTypeChange)="onAssignmentTypeChange($event)" />
</ng-container>
</div>
</form>
</ng-container>
</mat-expansion-panel>
</mat-accordion>

View File

@@ -19,21 +19,20 @@ import { ComponentFixture, TestBed } from '@angular/core/testing';
import { SimpleChange } from '@angular/core';
import { MatDialog } from '@angular/material/dialog';
import { of, Subject } from 'rxjs';
import { TASK_FILTERS_SERVICE_TOKEN } from '../../../../services/cloud-token.service';
import { LocalPreferenceCloudService } from '../../../../services/local-preference-cloud.service';
import { ProcessServiceCloudTestingModule } from '../../../../testing/process-service-cloud.testing.module';
import { AppsProcessCloudService } from '../../../../app/services/apps-process-cloud.service';
import { fakeApplicationInstance, fakeApplicationInstanceWithEnvironment } from '../../../../app/mock/app-model.mock';
import { TaskFiltersCloudModule } from '../../task-filters-cloud.module';
import { ServiceTaskFilterCloudService } from '../../services/service-task-filter-cloud.service';
import { TaskCloudService } from '../../../services/task-cloud.service';
import { fakeServiceFilter } from '../../mock/task-filters-cloud.mock';
import { TASK_FILTERS_SERVICE_TOKEN } from '../../../../../services/cloud-token.service';
import { LocalPreferenceCloudService } from '../../../../../services/local-preference-cloud.service';
import { ProcessServiceCloudTestingModule } from '../../../../../testing/process-service-cloud.testing.module';
import { AppsProcessCloudService } from '../../../../../app/services/apps-process-cloud.service';
import { fakeApplicationInstance, fakeApplicationInstanceWithEnvironment } from '../../../../../app/mock/app-model.mock';
import { ServiceTaskFilterCloudService } from '../../../services/service-task-filter-cloud.service';
import { TaskCloudService } from '../../../../services/task-cloud.service';
import { fakeServiceFilter } from '../../../mock/task-filters-cloud.mock';
import { EditServiceTaskFilterCloudComponent } from './edit-service-task-filter-cloud.component';
import { MatIconTestingModule } from '@angular/material/icon/testing';
import { ProcessDefinitionCloud } from '../../../../models/process-definition-cloud.model';
import { TaskFilterDialogCloudComponent } from '../task-filter-dialog/task-filter-dialog-cloud.component';
import { fakeEnvironmentList } from '../../../../common/mock/environment.mock';
import { mockApplicationTaskFilterProperties } from '../../mock/edit-task-filter-cloud.mock';
import { ProcessDefinitionCloud } from '../../../../../models/process-definition-cloud.model';
import { TaskFilterDialogCloudComponent } from '../../task-filter-dialog/task-filter-dialog-cloud.component';
import { fakeEnvironmentList } from '../../../../../common/mock/environment.mock';
import { mockApplicationTaskFilterProperties } from '../../../mock/edit-task-filter-cloud.mock';
import { HarnessLoader } from '@angular/cdk/testing';
import { TestbedHarnessEnvironment } from '@angular/cdk/testing/testbed';
import { MatExpansionPanelHarness } from '@angular/material/expansion/testing';
@@ -54,7 +53,7 @@ describe('EditServiceTaskFilterCloudComponent', () => {
beforeEach(() => {
TestBed.configureTestingModule({
imports: [ProcessServiceCloudTestingModule, TaskFiltersCloudModule, MatIconTestingModule],
imports: [ProcessServiceCloudTestingModule, MatIconTestingModule, EditServiceTaskFilterCloudComponent],
providers: [MatDialog, { provide: TASK_FILTERS_SERVICE_TOKEN, useClass: LocalPreferenceCloudService }]
});
fixture = TestBed.createComponent(EditServiceTaskFilterCloudComponent);

View File

@@ -17,15 +17,50 @@
import { Component, inject, ViewEncapsulation } from '@angular/core';
import { Observable } from 'rxjs';
import { ServiceTaskFilterCloudModel, TaskFilterAction, TaskFilterProperties } from '../../models/filter-cloud.model';
import { ServiceTaskFilterCloudService } from '../../services/service-task-filter-cloud.service';
import { BaseEditTaskFilterCloudComponent, DropdownOption } from './base-edit-task-filter-cloud.component';
import { ServiceTaskFilterCloudModel, TaskFilterAction, TaskFilterProperties } from '../../../models/filter-cloud.model';
import { ServiceTaskFilterCloudService } from '../../../services/service-task-filter-cloud.service';
import { BaseEditTaskFilterCloudComponent, DropdownOption } from '../base-edit-task-filter-cloud.component';
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
import { CommonModule } from '@angular/common';
import { TranslateModule } from '@ngx-translate/core';
import { MatExpansionModule } from '@angular/material/expansion';
import { MatButtonModule } from '@angular/material/button';
import { IconComponent } from '@alfresco/adf-core';
import { MatProgressSpinnerModule } from '@angular/material/progress-spinner';
import { ReactiveFormsModule } from '@angular/forms';
import { MatFormFieldModule } from '@angular/material/form-field';
import { MatSelectModule } from '@angular/material/select';
import { MatInputModule } from '@angular/material/input';
import { MatDatepickerModule } from '@angular/material/datepicker';
import { MatIconModule } from '@angular/material/icon';
import { MatCheckboxModule } from '@angular/material/checkbox';
import { DateRangeFilterComponent } from '../../../../../common/date-range-filter/date-range-filter.component';
import { PeopleCloudComponent } from '../../../../../people/components/people-cloud.component';
import { TaskAssignmentFilterCloudComponent } from '../../task-assignment-filter/task-assignment-filter.component';
@Component({
selector: 'adf-cloud-edit-service-task-filter',
templateUrl: './base-edit-task-filter-cloud.component.html',
styleUrls: ['./base-edit-task-filter-cloud.component.scss'],
standalone: true,
imports: [
CommonModule,
TranslateModule,
MatExpansionModule,
MatButtonModule,
IconComponent,
MatProgressSpinnerModule,
ReactiveFormsModule,
MatFormFieldModule,
MatSelectModule,
MatInputModule,
MatDatepickerModule,
MatIconModule,
MatCheckboxModule,
DateRangeFilterComponent,
PeopleCloudComponent,
TaskAssignmentFilterCloudComponent
],
templateUrl: './edit-service-task-filter-cloud.component.html',
styleUrls: ['./edit-service-task-filter-cloud.component.scss'],
encapsulation: ViewEncapsulation.None
})
export class EditServiceTaskFilterCloudComponent extends BaseEditTaskFilterCloudComponent<ServiceTaskFilterCloudModel> {
@@ -62,9 +97,7 @@ export class EditServiceTaskFilterCloudComponent extends BaseEditTaskFilterCloud
}
protected addFilter(filterToAdd: ServiceTaskFilterCloudModel): Observable<any> {
return this.serviceTaskFilterCloudService
.addFilter(filterToAdd)
.pipe(takeUntilDestroyed(this.destroyRef));
return this.serviceTaskFilterCloudService.addFilter(filterToAdd).pipe(takeUntilDestroyed(this.destroyRef));
}
isDisabledForDefaultFilters(action: TaskFilterAction): boolean {

View File

@@ -0,0 +1,97 @@
@import 'styles/flex';
.adf-edit-task-filter-checkbox {
font-size: var(--theme-subheading-2-font-size);
padding-top: 10px;
text-align: center;
flex: 1 23%;
}
.adf-edit-task-filter-date-error-container {
position: absolute;
height: 20px;
margin-top: 12px;
width: 100%;
& > div {
display: flex;
flex-flow: row;
justify-content: flex-start;
}
.adf-error-text {
padding-right: 8px;
height: 16px;
font-size: 10px;
line-height: 1.33;
color: var(--theme-warn-color);
width: auto;
}
.adf-error-icon {
font-size: 16px;
color: var(--theme-warn-color);
}
}
.adf-edit-task-filter-dateRange mat-grid-list {
width: 450px;
}
.adf {
&-cloud-edit-task-filter-loading-margin {
margin-left: calc((100% - 100px) / 2);
margin-right: calc((100% - 100px) / 2);
}
&-edit-task-filter-form {
flex-flow: row wrap;
display: flex;
place-content: center flex-start;
align-items: center;
:not(:last-child) {
margin-right: 10px;
}
mat-form-field,
adf-cloud-people,
adf-cloud-task-assignment-filter {
flex: 1 1 23%;
}
@include layout-bp(lt-sm) {
flex-flow: column;
:not(:last-child) {
margin-bottom: 10px;
margin-right: 0;
}
}
}
&-edit-task-filter {
&-header {
height: var(--adf-edit-task-and-service-filter-header-height);
&__title {
color: var(--adf-edit-task-and-service-filter-header-title-color);
}
&__description {
color: var(--adf-edit-task-and-service-filter-header-description-color);
place-content: center space-between;
}
}
&-content {
&__text-label {
color: var(--adf-edit-task-and-service-filter-content-text-label-color);
}
&__select-label {
color: var(--adf-edit-task-and-service-filter-content-select-label-color);
}
}
}
}

View File

@@ -20,20 +20,18 @@ import { By } from '@angular/platform-browser';
import { AlfrescoApiService } from '@alfresco/adf-content-services';
import { MatDialog } from '@angular/material/dialog';
import { of, Subject } from 'rxjs';
import { TASK_FILTERS_SERVICE_TOKEN } from '../../../../services/cloud-token.service';
import { LocalPreferenceCloudService } from '../../../../services/local-preference-cloud.service';
import { ProcessServiceCloudTestingModule } from '../../../../testing/process-service-cloud.testing.module';
import { AppsProcessCloudService } from '../../../../app/services/apps-process-cloud.service';
import { fakeApplicationInstance } from '../../../../app/mock/app-model.mock';
import { TaskFiltersCloudModule } from '../../task-filters-cloud.module';
import { TASK_FILTERS_SERVICE_TOKEN } from '../../../../../services/cloud-token.service';
import { LocalPreferenceCloudService } from '../../../../../services/local-preference-cloud.service';
import { ProcessServiceCloudTestingModule } from '../../../../../testing/process-service-cloud.testing.module';
import { AppsProcessCloudService } from '../../../../../app/services/apps-process-cloud.service';
import { fakeApplicationInstance } from '../../../../../app/mock/app-model.mock';
import { EditTaskFilterCloudComponent } from './edit-task-filter-cloud.component';
import { TaskFilterCloudService } from '../../services/task-filter-cloud.service';
import { TaskCloudService } from '../../../services/task-cloud.service';
import { fakeFilter } from '../../mock/task-filters-cloud.mock';
import { DateCloudFilterType } from '../../../../models/date-cloud-filter.model';
import { AssignmentType, TaskFilterCloudModel, TaskStatusFilter } from '../../models/filter-cloud.model';
import { PeopleCloudModule } from '../../../../people/people-cloud.module';
import { ProcessDefinitionCloud } from '../../../../models/process-definition-cloud.model';
import { TaskFilterCloudService } from '../../../services/task-filter-cloud.service';
import { TaskCloudService } from '../../../../services/task-cloud.service';
import { fakeFilter } from '../../../mock/task-filters-cloud.mock';
import { DateCloudFilterType } from '../../../../../models/date-cloud-filter.model';
import { AssignmentType, TaskFilterCloudModel, TaskStatusFilter } from '../../../models/filter-cloud.model';
import { ProcessDefinitionCloud } from '../../../../../models/process-definition-cloud.model';
import { MatIconTestingModule } from '@angular/material/icon/testing';
import {
mockAlfrescoApi,
@@ -46,17 +44,18 @@ import {
mockTaskFilterIdChange,
mockTaskFilterResponse,
mockTaskFilterResponseWithProcessInstanceIdNull
} from '../../mock/edit-task-filter-cloud.mock';
import { mockFoodUsers } from '../../../../people/mock/people-cloud.mock';
import { mockFoodGroups } from '../../../../group/mock/group-cloud.mock';
} from '../../../mock/edit-task-filter-cloud.mock';
import { mockFoodUsers } from '../../../../../people/mock/people-cloud.mock';
import { mockFoodGroups } from '../../../../../group/mock/group-cloud.mock';
import { SimpleChanges } from '@angular/core';
import { TaskFilterDialogCloudComponent } from '../task-filter-dialog/task-filter-dialog-cloud.component';
import { TaskFilterDialogCloudComponent } from '../../task-filter-dialog/task-filter-dialog-cloud.component';
import { set } from 'date-fns';
import { HarnessLoader } from '@angular/cdk/testing';
import { TestbedHarnessEnvironment } from '@angular/cdk/testing/testbed';
import { MatSelectHarness } from '@angular/material/select/testing';
import { MatExpansionPanelHarness } from '@angular/material/expansion/testing';
import { MatProgressSpinnerHarness } from '@angular/material/progress-spinner/testing';
import { PeopleCloudComponent } from '@alfresco/adf-process-services-cloud';
describe('EditTaskFilterCloudComponent', () => {
let loader: HarnessLoader;
@@ -74,7 +73,7 @@ describe('EditTaskFilterCloudComponent', () => {
beforeEach(() => {
TestBed.configureTestingModule({
imports: [ProcessServiceCloudTestingModule, TaskFiltersCloudModule, PeopleCloudModule, MatIconTestingModule],
imports: [ProcessServiceCloudTestingModule, PeopleCloudComponent, MatIconTestingModule, EditTaskFilterCloudComponent],
providers: [MatDialog, { provide: TASK_FILTERS_SERVICE_TOKEN, useClass: LocalPreferenceCloudService }]
});
fixture = TestBed.createComponent(EditTaskFilterCloudComponent);

View File

@@ -18,22 +18,52 @@
import { Component, inject, ViewEncapsulation } from '@angular/core';
import { map } from 'rxjs/operators';
import { Observable } from 'rxjs';
import {
TaskFilterAction,
TaskFilterCloudModel,
TaskFilterProperties,
TaskStatusFilter
} from '../../models/filter-cloud.model';
import { TaskFilterCloudService } from '../../services/task-filter-cloud.service';
import { DateCloudFilterType } from '../../../../models/date-cloud-filter.model';
import { BaseEditTaskFilterCloudComponent, DropdownOption } from './base-edit-task-filter-cloud.component';
import { TaskFilterAction, TaskFilterCloudModel, TaskFilterProperties, TaskStatusFilter } from '../../../models/filter-cloud.model';
import { TaskFilterCloudService } from '../../../services/task-filter-cloud.service';
import { DateCloudFilterType } from '../../../../../models/date-cloud-filter.model';
import { BaseEditTaskFilterCloudComponent, DropdownOption } from '../base-edit-task-filter-cloud.component';
import { set } from 'date-fns';
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
import { CommonModule } from '@angular/common';
import { TranslateModule } from '@ngx-translate/core';
import { MatExpansionModule } from '@angular/material/expansion';
import { MatButtonModule } from '@angular/material/button';
import { IconComponent } from '@alfresco/adf-core';
import { MatProgressSpinnerModule } from '@angular/material/progress-spinner';
import { MatFormFieldModule } from '@angular/material/form-field';
import { ReactiveFormsModule } from '@angular/forms';
import { MatSelectModule } from '@angular/material/select';
import { MatInputModule } from '@angular/material/input';
import { MatDatepickerModule } from '@angular/material/datepicker';
import { MatIconModule } from '@angular/material/icon';
import { MatCheckboxModule } from '@angular/material/checkbox';
import { DateRangeFilterComponent } from '../../../../../common/date-range-filter/date-range-filter.component';
import { PeopleCloudComponent } from '../../../../../people/components/people-cloud.component';
import { TaskAssignmentFilterCloudComponent } from '../../task-assignment-filter/task-assignment-filter.component';
@Component({
selector: 'adf-cloud-edit-task-filter',
templateUrl: './base-edit-task-filter-cloud.component.html',
styleUrls: ['./base-edit-task-filter-cloud.component.scss'],
standalone: true,
imports: [
CommonModule,
TranslateModule,
MatExpansionModule,
MatButtonModule,
IconComponent,
MatProgressSpinnerModule,
MatFormFieldModule,
ReactiveFormsModule,
MatSelectModule,
MatInputModule,
MatDatepickerModule,
MatIconModule,
MatCheckboxModule,
DateRangeFilterComponent,
PeopleCloudComponent,
TaskAssignmentFilterCloudComponent
],
templateUrl: './edit-task-filter-cloud.component.html',
styleUrls: ['./edit-task-filter-cloud.component.scss'],
encapsulation: ViewEncapsulation.None
})
export class EditTaskFilterCloudComponent extends BaseEditTaskFilterCloudComponent<TaskFilterCloudModel> {
@@ -51,21 +81,14 @@ export class EditTaskFilterCloudComponent extends BaseEditTaskFilterCloudCompone
}
protected getTaskFilterById(appName: string, id: string) {
return this.taskFilterCloudService
.getTaskFilterById(appName, id)
.pipe(
map(response => new TaskFilterCloudModel(response))
);
return this.taskFilterCloudService.getTaskFilterById(appName, id).pipe(map((response) => new TaskFilterCloudModel(response)));
}
createAndFilterProperties() {
const result = super.createAndFilterProperties();
if (this.hasLastModifiedProperty()) {
return [
...result,
...this.createLastModifiedProperty()
];
return [...result, ...this.createLastModifiedProperty()];
}
return result;
@@ -81,14 +104,11 @@ export class EditTaskFilterCloudComponent extends BaseEditTaskFilterCloudCompone
private setLastModifiedToFilter(formValues: TaskFilterCloudModel) {
if (formValues.lastModifiedTo && Date.parse(formValues.lastModifiedTo.toString())) {
const lastModifiedToFilterValue = set(
new Date(formValues.lastModifiedTo),
{
hours: 23,
minutes: 59,
seconds: 59
}
);
const lastModifiedToFilterValue = set(new Date(formValues.lastModifiedTo), {
hours: 23,
minutes: 59,
seconds: 59
});
formValues.lastModifiedTo = lastModifiedToFilterValue.toISOString();
}
}
@@ -102,9 +122,7 @@ export class EditTaskFilterCloudComponent extends BaseEditTaskFilterCloudCompone
}
protected addFilter(filterToAdd: TaskFilterCloudModel): Observable<any> {
return this.taskFilterCloudService
.addFilter(filterToAdd)
.pipe(takeUntilDestroyed(this.destroyRef));
return this.taskFilterCloudService.addFilter(filterToAdd).pipe(takeUntilDestroyed(this.destroyRef));
}
isDisabledForDefaultFilters(action: TaskFilterAction): boolean {
@@ -250,7 +268,7 @@ export class EditTaskFilterCloudComponent extends BaseEditTaskFilterCloudCompone
label: 'ADF_CLOUD_EDIT_TASK_FILTER.LABEL.DUE_DATE',
type: 'date-range',
key: 'dueDateRange',
attributes: { dateType: 'dueDateType', from: '_dueDateFrom', to: '_dueDateTo'},
attributes: { dateType: 'dueDateType', from: '_dueDateFrom', to: '_dueDateTo' },
value: {
dueDateType: this.taskFilter.dueDateType || null,
_dueDateFrom: this.taskFilter.dueDateFrom || null,
@@ -268,7 +286,7 @@ export class EditTaskFilterCloudComponent extends BaseEditTaskFilterCloudCompone
label: 'ADF_CLOUD_EDIT_TASK_FILTER.LABEL.COMPLETED_DATE',
type: 'date-range',
key: 'completedDateRange',
attributes: { dateType: 'completedDateType', from: '_completedFrom', to: '_completedTo'},
attributes: { dateType: 'completedDateType', from: '_completedFrom', to: '_completedTo' },
value: {
completedDateType: this.taskFilter.completedDateType || null,
_completedFrom: this.taskFilter.completedFrom || null,
@@ -279,7 +297,7 @@ export class EditTaskFilterCloudComponent extends BaseEditTaskFilterCloudCompone
label: 'ADF_CLOUD_EDIT_TASK_FILTER.LABEL.CREATED_DATE',
type: 'date-range',
key: 'createdDateRange',
attributes: { dateType: 'createdDateType', from: '_createdFrom', to: '_createdTo'},
attributes: { dateType: 'createdDateType', from: '_createdFrom', to: '_createdTo' },
value: {
createdDateType: this.taskFilter.createdDateType || null,
_createdFrom: this.taskFilter.createdFrom || null,
@@ -297,7 +315,7 @@ export class EditTaskFilterCloudComponent extends BaseEditTaskFilterCloudCompone
label: 'ADF_CLOUD_EDIT_TASK_FILTER.LABEL.ASSIGNMENT',
type: 'assignment',
key: 'assignment',
attributes: { assignedUsers: 'assignedUsers', candidateGroups: 'candidateGroups'},
attributes: { assignedUsers: 'assignedUsers', candidateGroups: 'candidateGroups' },
value: {
assignedUsers: this.taskFilter.assignedUsers || [],
candidateGroups: this.taskFilter.candidateGroups || []

View File

@@ -18,13 +18,12 @@
import { SimpleChange } from '@angular/core';
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { of, throwError } from 'rxjs';
import { TASK_FILTERS_SERVICE_TOKEN } from '../../../services/cloud-token.service';
import { LocalPreferenceCloudService } from '../../../services/local-preference-cloud.service';
import { TASK_FILTERS_SERVICE_TOKEN } from '../../../../services/cloud-token.service';
import { LocalPreferenceCloudService } from '../../../../services/local-preference-cloud.service';
import { By } from '@angular/platform-browser';
import { ProcessServiceCloudTestingModule } from '../../../testing/process-service-cloud.testing.module';
import { TaskFiltersCloudModule } from '../task-filters-cloud.module';
import { fakeGlobalServiceFilters } from '../mock/task-filters-cloud.mock';
import { ServiceTaskFilterCloudService } from '../services/service-task-filter-cloud.service';
import { ProcessServiceCloudTestingModule } from '../../../../testing/process-service-cloud.testing.module';
import { fakeGlobalServiceFilters } from '../../mock/task-filters-cloud.mock';
import { ServiceTaskFilterCloudService } from '../../services/service-task-filter-cloud.service';
import { ServiceTaskFiltersCloudComponent } from './service-task-filters-cloud.component';
describe('ServiceTaskFiltersCloudComponent', () => {
@@ -36,7 +35,7 @@ describe('ServiceTaskFiltersCloudComponent', () => {
beforeEach(() => {
TestBed.configureTestingModule({
imports: [ProcessServiceCloudTestingModule, TaskFiltersCloudModule],
imports: [ProcessServiceCloudTestingModule, ServiceTaskFiltersCloudComponent],
providers: [{ provide: TASK_FILTERS_SERVICE_TOKEN, useClass: LocalPreferenceCloudService }]
});
fixture = TestBed.createComponent(ServiceTaskFiltersCloudComponent);

View File

@@ -15,27 +15,24 @@
* limitations under the License.
*/
import {
Component,
EventEmitter,
inject,
OnChanges,
OnInit,
Output,
SimpleChanges,
ViewEncapsulation
} from '@angular/core';
import { Component, EventEmitter, inject, OnChanges, OnInit, Output, SimpleChanges, ViewEncapsulation } from '@angular/core';
import { Observable } from 'rxjs';
import { FilterParamsModel, ServiceTaskFilterCloudModel } from '../models/filter-cloud.model';
import { BaseTaskFiltersCloudComponent } from './base-task-filters-cloud.component';
import { ServiceTaskFilterCloudService } from '../services/service-task-filter-cloud.service';
import { TranslationService } from '@alfresco/adf-core';
import { FilterParamsModel, ServiceTaskFilterCloudModel } from '../../models/filter-cloud.model';
import { BaseTaskFiltersCloudComponent } from '../base-task-filters-cloud.component';
import { ServiceTaskFilterCloudService } from '../../services/service-task-filter-cloud.service';
import { IconComponent, TranslationService } from '@alfresco/adf-core';
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
import { CommonModule } from '@angular/common';
import { TranslateModule } from '@ngx-translate/core';
import { MatListModule } from '@angular/material/list';
import { MatProgressSpinnerModule } from '@angular/material/progress-spinner';
@Component({
selector: 'adf-cloud-service-task-filters',
templateUrl: './base-task-filters-cloud.component.html',
styleUrls: ['./base-task-filters-cloud.component.scss'],
standalone: true,
imports: [CommonModule, TranslateModule, MatListModule, IconComponent, MatProgressSpinnerModule],
templateUrl: './service-task-filters-cloud.component.html',
styleUrls: ['./service-task-filters-cloud.component.scss'],
encapsulation: ViewEncapsulation.None
})
export class ServiceTaskFiltersCloudComponent extends BaseTaskFiltersCloudComponent implements OnInit, OnChanges {

View File

@@ -21,7 +21,6 @@
<adf-cloud-group *ngIf="isCandidateGroupsType()"
class="adf-group-cloud-filter"
data-automation-id="adf-group-cloud-candidate-groups-filter"
[mode]="'multiple'"
[appName]="appName"
[preSelectGroups]="candidateGroups"
[mode]="taskFilterProperty.selectionMode"

View File

@@ -17,8 +17,6 @@
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { TaskAssignmentFilterCloudComponent } from './task-assignment-filter.component';
import { GroupCloudModule } from '../../../../group/group-cloud.module';
import { TaskFiltersCloudModule } from '../../task-filters-cloud.module';
import { AssignmentType, TaskStatusFilter } from '../../models/filter-cloud.model';
import { IdentityUserService } from '../../../../people/services/identity-user.service';
import { By } from '@angular/platform-browser';
@@ -30,6 +28,7 @@ import { TestbedHarnessEnvironment } from '@angular/cdk/testing/testbed';
import { MatSelectHarness } from '@angular/material/select/testing';
import { MatFormFieldHarness } from '@angular/material/form-field/testing';
import { ProcessServiceCloudTestingModule } from '../../../../testing/process-service-cloud.testing.module';
import { GroupCloudComponent } from '@alfresco/adf-process-services-cloud';
describe('TaskAssignmentFilterComponent', () => {
let component: TaskAssignmentFilterCloudComponent;
@@ -58,7 +57,7 @@ describe('TaskAssignmentFilterComponent', () => {
beforeEach(() => {
TestBed.configureTestingModule({
imports: [ProcessServiceCloudTestingModule, GroupCloudModule, TaskFiltersCloudModule]
imports: [ProcessServiceCloudTestingModule, GroupCloudComponent, TaskAssignmentFilterCloudComponent]
});
});

View File

@@ -16,21 +16,26 @@
*/
import { Component, Input, Output, EventEmitter, OnInit, OnChanges, SimpleChanges } from '@angular/core';
import { MatSelectChange } from '@angular/material/select';
import { MatSelectChange, MatSelectModule } from '@angular/material/select';
import { AssignmentType, TaskFilterProperties, TaskStatusFilter } from '../../models/filter-cloud.model';
import { IdentityUserModel } from '../../../../people/models/identity-user.model';
import { IdentityUserService } from '../../../../people/services/identity-user.service';
import { IdentityGroupModel } from '../../../../group/models/identity-group.model';
import { DropdownOption } from '../edit-task-filters/base-edit-task-filter-cloud.component';
import { FormControl } from '@angular/forms';
import { FormControl, FormsModule } from '@angular/forms';
import { CommonModule } from '@angular/common';
import { TranslateModule } from '@ngx-translate/core';
import { GroupCloudComponent } from '../../../../group/components/group-cloud.component';
import { PeopleCloudComponent } from '../../../../people/components/people-cloud.component';
@Component({
selector: 'adf-cloud-task-assignment-filter',
standalone: true,
imports: [CommonModule, GroupCloudComponent, TranslateModule, MatSelectModule, FormsModule, PeopleCloudComponent],
templateUrl: './task-assignment-filter.component.html',
styleUrls: ['./task-assignment-filter.component.scss']
})
export class TaskAssignmentFilterCloudComponent implements OnInit, OnChanges {
@Input() appName: string;
@Input() taskFilterProperty: TaskFilterProperties;

View File

@@ -18,7 +18,6 @@
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { MatDialogRef, MAT_DIALOG_DATA } from '@angular/material/dialog';
import { TaskFilterDialogCloudComponent } from './task-filter-dialog-cloud.component';
import { TaskFiltersCloudModule } from '../../task-filters-cloud.module';
import { ProcessServiceCloudTestingModule } from '../../../../testing/process-service-cloud.testing.module';
describe('TaskFilterDialogCloudComponent', () => {
@@ -36,7 +35,7 @@ describe('TaskFilterDialogCloudComponent', () => {
beforeEach(() => {
TestBed.configureTestingModule({
imports: [ProcessServiceCloudTestingModule, TaskFiltersCloudModule],
imports: [ProcessServiceCloudTestingModule, TaskFilterDialogCloudComponent],
providers: [
{ provide: MatDialogRef, useValue: mockDialogRef },
{ provide: MAT_DIALOG_DATA, useValue: mockDialogData }

View File

@@ -16,17 +16,23 @@
*/
import { Component, Inject, OnInit, ViewEncapsulation } from '@angular/core';
import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog';
import { AbstractControl, UntypedFormBuilder, UntypedFormGroup, Validators } from '@angular/forms';
import { MAT_DIALOG_DATA, MatDialogModule, MatDialogRef } from '@angular/material/dialog';
import { AbstractControl, ReactiveFormsModule, UntypedFormBuilder, UntypedFormGroup, Validators } from '@angular/forms';
import { CommonModule } from '@angular/common';
import { TranslateModule } from '@ngx-translate/core';
import { MatInputModule } from '@angular/material/input';
import { MatCardModule } from '@angular/material/card';
import { MatButtonModule } from '@angular/material/button';
@Component({
selector: 'adf-cloud-task-filter-dialog',
templateUrl: './task-filter-dialog-cloud.component.html',
styleUrls: ['./task-filter-dialog-cloud.component.scss'],
encapsulation: ViewEncapsulation.None
selector: 'adf-cloud-task-filter-dialog',
standalone: true,
imports: [CommonModule, TranslateModule, MatInputModule, ReactiveFormsModule, MatDialogModule, MatCardModule, MatButtonModule],
templateUrl: './task-filter-dialog-cloud.component.html',
styleUrls: ['./task-filter-dialog-cloud.component.scss'],
encapsulation: ViewEncapsulation.None
})
export class TaskFilterDialogCloudComponent implements OnInit {
// eslint-disable-next-line @typescript-eslint/naming-convention
public static ACTION_SAVE = 'SAVE';
defaultIcon = 'inbox';
@@ -36,8 +42,8 @@ export class TaskFilterDialogCloudComponent implements OnInit {
constructor(
private fb: UntypedFormBuilder,
public dialogRef: MatDialogRef<TaskFilterDialogCloudComponent>,
@Inject(MAT_DIALOG_DATA) public data) {
}
@Inject(MAT_DIALOG_DATA) public data
) {}
ngOnInit() {
this.filterForm = this.fb.group({

View File

@@ -0,0 +1,35 @@
<mat-action-list class="adf-task-filters" *ngIf="filters$ | async as filterList; else loading">
<button
*ngFor="let filter of filterList"
mat-list-item
(click)="onFilterClick(filter)"
[attr.aria-label]="filter.name | translate"
[id]="filter.id"
[attr.data-automation-id]="filter.key + '_filter'"
[class.adf-active]="currentFilter === filter">
<div class="adf-task-filters__entry">
<div class="adf-task-filters__entry-label">
<adf-icon data-automation-id="adf-filter-icon"
*ngIf="showIcons"
[value]="filter.icon"
/>
<span data-automation-id="adf-filter-label">
{{ filter.name | translate }}
</span>
</div>
<span *ngIf="counters[filter.key]"
[attr.data-automation-id]="filter.key + '_filter-counter'"
class="adf-task-filters__entry-counter"
[class.adf-active]="wasFilterUpdated(filter.key)">
{{ counters[filter.key] }}
</span>
</div>
</button>
</mat-action-list>
<ng-template #loading>
<ng-container>
<div class="adf-app-list-spinner">
<mat-spinner />
</div>
</ng-container>
</ng-template>

View File

@@ -0,0 +1,39 @@
.adf-task-filters {
margin-right: calc(-1 * var(--adf-theme-spacing));
&__entry {
font-size: var(--theme-body-1-font-size);
color: var(--adf-theme-foreground-text-color-054);
display: flex;
justify-content: space-between;
align-items: center;
flex: 1;
height: 100%;
&:hover {
color: var(--theme-primary-color);
}
}
&__entry-label {
display: flex;
flex: 1;
align-items: center;
gap: var(--adf-theme-spacing);
}
&__entry-counter {
padding: 0 5px;
border-radius: 15px;
&.adf-active {
background-color: var(--theme-accent-color);
color: var(--theme-accent-color-default-contrast);
font-size: smaller;
}
}
.adf-active .adf-task-filters__entry-label {
color: var(--theme-primary-color);
}
}

View File

@@ -20,18 +20,17 @@ import { SimpleChange } from '@angular/core';
import { ComponentFixture, TestBed, fakeAsync } from '@angular/core/testing';
import { By } from '@angular/platform-browser';
import { first, of, throwError } from 'rxjs';
import { TASK_FILTERS_SERVICE_TOKEN } from '../../../services/cloud-token.service';
import { LocalPreferenceCloudService } from '../../../services/local-preference-cloud.service';
import { ProcessServiceCloudTestingModule } from '../../../testing/process-service-cloud.testing.module';
import { defaultTaskFiltersMock, fakeGlobalFilter, taskNotifications } from '../mock/task-filters-cloud.mock';
import { TaskFilterCloudService } from '../services/task-filter-cloud.service';
import { TaskFiltersCloudModule } from '../task-filters-cloud.module';
import { TASK_FILTERS_SERVICE_TOKEN } from '../../../../services/cloud-token.service';
import { LocalPreferenceCloudService } from '../../../../services/local-preference-cloud.service';
import { ProcessServiceCloudTestingModule } from '../../../../testing/process-service-cloud.testing.module';
import { defaultTaskFiltersMock, fakeGlobalFilter, taskNotifications } from '../../mock/task-filters-cloud.mock';
import { TaskFilterCloudService } from '../../services/task-filter-cloud.service';
import { TaskFiltersCloudComponent } from './task-filters-cloud.component';
import { TaskListCloudService } from '../../task-list/services/task-list-cloud.service';
import { TaskListCloudService } from '../../../task-list/services/task-list-cloud.service';
import { HarnessLoader } from '@angular/cdk/testing';
import { MatActionListItemHarness } from '@angular/material/list/testing';
import { TestbedHarnessEnvironment } from '@angular/cdk/testing/testbed';
import { TaskFilterCloudAdapter } from '../../../models/filter-cloud-model';
import { TaskFilterCloudAdapter } from '../../../../models/filter-cloud-model';
describe('TaskFiltersCloudComponent', () => {
let loader: HarnessLoader;
@@ -47,7 +46,7 @@ describe('TaskFiltersCloudComponent', () => {
const configureTestingModule = (searchApiMethod: 'GET' | 'POST') => {
TestBed.configureTestingModule({
imports: [ProcessServiceCloudTestingModule, TaskFiltersCloudModule],
imports: [ProcessServiceCloudTestingModule, TaskFiltersCloudComponent],
providers: [{ provide: TASK_FILTERS_SERVICE_TOKEN, useClass: LocalPreferenceCloudService }]
});
taskFilterService = TestBed.inject(TaskFilterCloudService);

View File

@@ -15,33 +15,29 @@
* limitations under the License.
*/
import {
Component,
EventEmitter,
inject,
Input,
OnChanges,
OnInit,
Output,
SimpleChanges,
ViewEncapsulation
} from '@angular/core';
import { Component, EventEmitter, inject, Input, OnChanges, OnInit, Output, SimpleChanges, ViewEncapsulation } from '@angular/core';
import { Observable } from 'rxjs';
import { TaskFilterCloudService } from '../services/task-filter-cloud.service';
import { FilterParamsModel, TaskFilterCloudModel } from '../models/filter-cloud.model';
import { AppConfigService, TranslationService } from '@alfresco/adf-core';
import { TaskFilterCloudService } from '../../services/task-filter-cloud.service';
import { FilterParamsModel, TaskFilterCloudModel } from '../../models/filter-cloud.model';
import { AppConfigService, IconComponent, TranslationService } from '@alfresco/adf-core';
import { debounceTime, tap } from 'rxjs/operators';
import { BaseTaskFiltersCloudComponent } from './base-task-filters-cloud.component';
import { TaskDetailsCloudModel } from '../../start-task/models/task-details-cloud.model';
import { TaskCloudEngineEvent } from '../../../models/engine-event-cloud.model';
import { TaskListCloudService } from '../../task-list/services/task-list-cloud.service';
import { TaskFilterCloudAdapter } from '../../../models/filter-cloud-model';
import { BaseTaskFiltersCloudComponent } from '../base-task-filters-cloud.component';
import { TaskDetailsCloudModel } from '../../../models/task-details-cloud.model';
import { TaskCloudEngineEvent } from '../../../../models/engine-event-cloud.model';
import { TaskListCloudService } from '../../../task-list/services/task-list-cloud.service';
import { TaskFilterCloudAdapter } from '../../../../models/filter-cloud-model';
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
import { MatProgressSpinnerModule } from '@angular/material/progress-spinner';
import { TranslateModule } from '@ngx-translate/core';
import { CommonModule } from '@angular/common';
import { MatListModule } from '@angular/material/list';
@Component({
selector: 'adf-cloud-task-filters',
templateUrl: './base-task-filters-cloud.component.html',
styleUrls: ['./base-task-filters-cloud.component.scss'],
standalone: true,
imports: [CommonModule, MatProgressSpinnerModule, TranslateModule, IconComponent, MatListModule],
templateUrl: './task-filters-cloud.component.html',
styleUrls: ['./task-filters-cloud.component.scss'],
encapsulation: ViewEncapsulation.None
})
export class TaskFiltersCloudComponent extends BaseTaskFiltersCloudComponent implements OnInit, OnChanges {
@@ -101,8 +97,8 @@ export class TaskFiltersCloudComponent extends BaseTaskFiltersCloudComponent imp
getFilters(appName: string): void {
this.filters$ = this.taskFilterCloudService.getTaskListFilters(appName);
this.filters$.pipe(takeUntilDestroyed(this.destroyRef)).subscribe(
(res) => {
this.filters$.pipe(takeUntilDestroyed(this.destroyRef)).subscribe({
next: (res) => {
this.resetFilter();
this.filters = res || [];
this.initFilterCounters();
@@ -110,10 +106,10 @@ export class TaskFiltersCloudComponent extends BaseTaskFiltersCloudComponent imp
this.updateFilterCounters();
this.success.emit(res);
},
(err: any) => {
error: (err) => {
this.error.emit(err);
}
);
});
}
/**
@@ -164,8 +160,8 @@ export class TaskFiltersCloudComponent extends BaseTaskFiltersCloudComponent imp
this.taskFilterCloudService
.getTaskNotificationSubscription(this.appName)
.pipe(debounceTime(1000))
.subscribe((result: TaskCloudEngineEvent[]) => {
result.map((taskEvent: TaskCloudEngineEvent) => {
.subscribe((result) => {
result.forEach((taskEvent) => {
this.checkFilterCounter(taskEvent.entity);
});
@@ -176,7 +172,7 @@ export class TaskFiltersCloudComponent extends BaseTaskFiltersCloudComponent imp
}
checkFilterCounter(filterNotification: TaskDetailsCloudModel) {
this.filters.map((filter) => {
this.filters.forEach((filter) => {
if (this.isFilterPresent(filter, filterNotification)) {
this.addToUpdatedCounters(filter.key);
}
@@ -238,6 +234,7 @@ export class TaskFiltersCloudComponent extends BaseTaskFiltersCloudComponent imp
}
/**
* @deprecated unused method
* Select as default task filter the first in the list
*/
public selectDefaultTaskFilter() {

View File

@@ -15,7 +15,7 @@
* limitations under the License.
*/
import { TaskDetailsCloudModel } from '../../../task/start-task/models/task-details-cloud.model';
import { TaskDetailsCloudModel } from '../../models/task-details-cloud.model';
import { assignedTaskDetailsCloudMock } from '../../task-header/mocks/task-details-cloud.mock';
import { TaskFilterCloudModel, ServiceTaskFilterCloudModel, AssignmentType, TaskStatusFilter } from '../models/filter-cloud.model';

View File

@@ -15,14 +15,13 @@
* limitations under the License.
*/
export * from './components/task-filters-cloud.component';
export * from './components/service-task-filters-cloud.component';
export * from './components/edit-task-filters/edit-task-filter-cloud.component';
export * from './components/edit-task-filters/edit-service-task-filter-cloud.component';
export * from './components/edit-task-filters/edit-task-filter/edit-task-filter-cloud.component';
export * from './components/edit-task-filters/edit-service-task-filter/edit-service-task-filter-cloud.component';
export * from './components/service-task-filters/service-task-filters-cloud.component';
export * from './components/task-assignment-filter/task-assignment-filter.component';
export * from './components/task-filter-dialog/task-filter-dialog-cloud.component';
export * from './components/task-filters/task-filters-cloud.component';
export * from './models/filter-cloud.model';
export * from './services/task-filter-cloud.service';
export * from './services/service-task-filter-cloud.service';
export * from './task-filters-cloud.module';

View File

@@ -16,45 +16,25 @@
*/
import { NgModule } from '@angular/core';
import { FormsModule, ReactiveFormsModule } from '@angular/forms';
import { CommonModule } from '@angular/common';
import { TaskFiltersCloudComponent } from './components/task-filters-cloud.component';
import { MaterialModule } from '../../material.module';
import { CoreModule } from '@alfresco/adf-core';
import { HttpClientModule } from '@angular/common/http';
import { ProcessCommonModule } from '../../common/process-common.module';
import { PeopleCloudModule } from '../../people/people-cloud.module';
import { EditServiceTaskFilterCloudComponent } from './components/edit-task-filters/edit-service-task-filter-cloud.component';
import { EditTaskFilterCloudComponent } from './components/edit-task-filters/edit-task-filter-cloud.component';
import { TaskFiltersCloudComponent } from './components/task-filters/task-filters-cloud.component';
import { EditServiceTaskFilterCloudComponent } from './components/edit-task-filters/edit-service-task-filter/edit-service-task-filter-cloud.component';
import { EditTaskFilterCloudComponent } from './components/edit-task-filters/edit-task-filter/edit-task-filter-cloud.component';
import { TaskFilterDialogCloudComponent } from './components/task-filter-dialog/task-filter-dialog-cloud.component';
import { ServiceTaskFiltersCloudComponent } from './components/service-task-filters-cloud.component';
import { ServiceTaskFiltersCloudComponent } from './components/service-task-filters/service-task-filters-cloud.component';
import { TaskAssignmentFilterCloudComponent } from './components/task-assignment-filter/task-assignment-filter.component';
import { GroupCloudModule } from '../../group/group-cloud.module';
import { MatProgressSpinnerModule } from '@angular/material/progress-spinner';
import { APP_LIST_CLOUD_DIRECTIVES } from '../../app/app-list-cloud.module';
export const TASK_FILTERS_CLOUD_DIRECTIVES = [
TaskFilterDialogCloudComponent,
TaskFiltersCloudComponent,
ServiceTaskFiltersCloudComponent,
EditTaskFilterCloudComponent,
TaskAssignmentFilterCloudComponent,
EditServiceTaskFilterCloudComponent
] as const;
/** @deprecated use ...TASK_FILTERS_CLOUD_DIRECTIVES instead */
@NgModule({
imports: [
FormsModule,
ReactiveFormsModule,
HttpClientModule,
CommonModule,
MaterialModule,
...APP_LIST_CLOUD_DIRECTIVES,
CoreModule,
GroupCloudModule,
ProcessCommonModule,
PeopleCloudModule,
MatProgressSpinnerModule
],
declarations: [
TaskFiltersCloudComponent,
ServiceTaskFiltersCloudComponent,
EditTaskFilterCloudComponent,
EditServiceTaskFilterCloudComponent,
TaskFilterDialogCloudComponent,
TaskAssignmentFilterCloudComponent
],
exports: [TaskFiltersCloudComponent, ServiceTaskFiltersCloudComponent, EditTaskFilterCloudComponent, EditServiceTaskFilterCloudComponent]
imports: [...TASK_FILTERS_CLOUD_DIRECTIVES],
exports: [...TASK_FILTERS_CLOUD_DIRECTIVES]
})
export class TaskFiltersCloudModule {}

View File

@@ -31,7 +31,7 @@ import {
TASK_RELEASE_PERMISSION,
TASK_VIEW_PERMISSION,
TaskDetailsCloudModel
} from '../../../start-task/models/task-details-cloud.model';
} from '../../../models/task-details-cloud.model';
import { UserTaskCloudButtonsComponent } from '../user-task-cloud-buttons/user-task-cloud-buttons.component';
import { TaskFormCloudComponent } from './task-form-cloud.component';

View File

@@ -23,7 +23,7 @@ import { DateCloudWidgetComponent } from '../../../../form/components/widgets/da
import { DropdownCloudWidgetComponent } from '../../../../form/components/widgets/dropdown/dropdown-cloud.widget';
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 { TaskDetailsCloudModel } from '../../../models/task-details-cloud.model';
import { CommonModule } from '@angular/common';
import { UserTaskCloudButtonsComponent } from '../user-task-cloud-buttons/user-task-cloud-buttons.component';
import { FormCustomOutcomesComponent } from '../../../../form/components/form-cloud-custom-outcomes.component';

View File

@@ -17,16 +17,18 @@
import { Component, ContentChildren, ViewChild } from '@angular/core';
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { TaskCloudService } from '../services/task-cloud.service';
import { TaskCloudService } from '../../../../services/task-cloud.service';
import { of, throwError } from 'rxjs';
import { ClaimTaskCloudDirective } from './claim-task-cloud.directive';
import { taskClaimCloudMock } from '../task-header/mocks/fake-claim-task.mock';
import { ProcessServiceCloudTestingModule } from '../../testing/process-service-cloud.testing.module';
import { taskClaimCloudMock } from '../../../../task-header/mocks/fake-claim-task.mock';
import { ProcessServiceCloudTestingModule } from '../../../../../testing/process-service-cloud.testing.module';
import { By } from '@angular/platform-browser';
describe('ClaimTaskCloudDirective', () => {
@Component({
selector: 'adf-cloud-claim-test-component',
standalone: true,
imports: [ClaimTaskCloudDirective],
template: '<button adf-cloud-claim-task [taskId]="taskMock" [appName]="appNameMock" (error)="onError($event)"></button>'
})
class TestComponent {
@@ -46,8 +48,8 @@ describe('ClaimTaskCloudDirective', () => {
beforeEach(() => {
TestBed.configureTestingModule({
imports: [ProcessServiceCloudTestingModule],
declarations: [TestComponent]
imports: [ProcessServiceCloudTestingModule, TestComponent],
declarations: []
});
taskCloudService = TestBed.inject(TaskCloudService);
fixture = TestBed.createComponent(TestComponent);
@@ -95,6 +97,8 @@ describe('ClaimTaskCloudDirective', () => {
describe('Claim Task Directive validation errors', () => {
@Component({
selector: 'adf-cloud-claim-no-fields-validation-component',
standalone: true,
imports: [ClaimTaskCloudDirective],
template: '<button adf-cloud-claim-task></button>'
})
class ClaimTestMissingInputDirectiveComponent {
@@ -108,6 +112,8 @@ describe('Claim Task Directive validation errors', () => {
@Component({
selector: 'adf-cloud-claim-no-taskid-validation-component',
standalone: true,
imports: [ClaimTaskCloudDirective],
template: '<button adf-cloud-claim-task [appName]="appName"></button>'
})
class ClaimTestMissingTaskIdDirectiveComponent {
@@ -119,6 +125,8 @@ describe('Claim Task Directive validation errors', () => {
@Component({
selector: 'adf-cloud-claim-undefined-appname-component',
standalone: true,
imports: [ClaimTaskCloudDirective],
template: '<button adf-cloud-claim-task [taskId]="taskMock" [appName]="appNameUndefined"></button>'
})
class ClaimTestInvalidAppNameUndefinedDirectiveComponent {
@@ -131,6 +139,8 @@ describe('Claim Task Directive validation errors', () => {
@Component({
selector: 'adf-cloud-claim-null-appname-component',
standalone: true,
imports: [ClaimTaskCloudDirective],
template: '<button adf-cloud-claim-task [taskId]="taskMock" [appName]="appNameNull"></button>'
})
class ClaimTestInvalidAppNameNullDirectiveComponent {
@@ -145,8 +155,8 @@ describe('Claim Task Directive validation errors', () => {
beforeEach(() => {
TestBed.configureTestingModule({
imports: [ProcessServiceCloudTestingModule],
declarations: [
imports: [
ProcessServiceCloudTestingModule,
ClaimTestMissingTaskIdDirectiveComponent,
ClaimTestInvalidAppNameUndefinedDirectiveComponent,
ClaimTestInvalidAppNameNullDirectiveComponent,

View File

@@ -16,8 +16,8 @@
*/
import { Directive, Input, HostListener, Output, EventEmitter, OnInit, ElementRef, Renderer2 } from '@angular/core';
import { IdentityUserService } from '../../people/services/identity-user.service';
import { TaskCloudService } from '../services/task-cloud.service';
import { IdentityUserService } from '../../../../../people/services/identity-user.service';
import { TaskCloudService } from '../../../../services/task-cloud.service';
@Directive({
// eslint-disable-next-line @angular-eslint/directive-selector

View File

@@ -17,11 +17,11 @@
import { Component, ContentChildren, ViewChild } from '@angular/core';
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { TaskCloudService } from '../services/task-cloud.service';
import { TaskCloudService } from '../../../../services/task-cloud.service';
import { of, throwError } from 'rxjs';
import { UnClaimTaskCloudDirective } from './unclaim-task-cloud.directive';
import { taskClaimCloudMock } from '../task-header/mocks/fake-claim-task.mock';
import { ProcessServiceCloudTestingModule } from '../../testing/process-service-cloud.testing.module';
import { taskClaimCloudMock } from '../../../../task-header/mocks/fake-claim-task.mock';
import { ProcessServiceCloudTestingModule } from '../../../../../testing/process-service-cloud.testing.module';
import { By } from '@angular/platform-browser';
describe('UnClaimTaskCloudDirective', () => {

View File

@@ -16,7 +16,7 @@
*/
import { Directive, Input, HostListener, Output, EventEmitter, OnInit, ElementRef, Renderer2 } from '@angular/core';
import { TaskCloudService } from '../services/task-cloud.service';
import { TaskCloudService } from '../../../../services/task-cloud.service';
@Directive({
// eslint-disable-next-line @angular-eslint/directive-selector

View File

@@ -19,8 +19,8 @@ import { Component, EventEmitter, Input, Output } from '@angular/core';
import { CommonModule } from '@angular/common';
import { TranslateModule } from '@ngx-translate/core';
import { MatButtonModule } from '@angular/material/button';
import { UnClaimTaskCloudDirective } from '../../../directives/unclaim-task-cloud.directive';
import { ClaimTaskCloudDirective } from '../../../directives/claim-task-cloud.directive';
import { UnClaimTaskCloudDirective } from './unclaim-task/unclaim-task-cloud.directive';
import { ClaimTaskCloudDirective } from './claim-task/claim-task-cloud.directive';
@Component({
selector: 'adf-cloud-user-task-cloud-buttons',

View File

@@ -19,9 +19,9 @@ import { Component, ViewChild, ContentChildren } from '@angular/core';
import { CompleteTaskDirective } from './complete-task.directive';
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { of, throwError } from 'rxjs';
import { taskCompleteCloudMock } from '../task-header/mocks/fake-complete-task.mock';
import { TaskCloudService } from '../services/task-cloud.service';
import { ProcessServiceCloudTestingModule } from '../../testing/process-service-cloud.testing.module';
import { taskCompleteCloudMock } from '../../../../task-header/mocks/fake-complete-task.mock';
import { TaskCloudService } from '../../../../services/task-cloud.service';
import { ProcessServiceCloudTestingModule } from '../../../../../testing/process-service-cloud.testing.module';
import { By } from '@angular/platform-browser';
describe('CompleteTaskDirective', () => {

View File

@@ -16,7 +16,7 @@
*/
import { Directive, Input, HostListener, Output, EventEmitter, OnInit, ElementRef, Renderer2 } from '@angular/core';
import { TaskCloudService } from '../services/task-cloud.service';
import { TaskCloudService } from '../../../../services/task-cloud.service';
@Directive({
// eslint-disable-next-line @angular-eslint/directive-selector

View File

@@ -20,7 +20,7 @@ import { Component, DestroyRef, EventEmitter, inject, Input, OnChanges, OnInit,
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 { TaskDetailsCloudModel } from '../../../models/task-details-cloud.model';
import { TaskFormCloudComponent } from '../task-form-cloud/task-form-cloud.component';
import { CommonModule } from '@angular/common';
import { MatProgressSpinnerModule } from '@angular/material/progress-spinner';
@@ -29,7 +29,7 @@ import { TranslateModule } from '@ngx-translate/core';
import { MatButtonModule } from '@angular/material/button';
import { MatCardModule } from '@angular/material/card';
import { TaskScreenCloudComponent } from '../../../../screen/components/screen-cloud/screen-cloud.component';
import { CompleteTaskDirective } from '../../../directives/complete-task.directive';
import { CompleteTaskDirective } from './complete-task/complete-task.directive';
const TaskTypes = {
Form: 'form',

View File

@@ -17,5 +17,5 @@
export * from './components/task-form-cloud/task-form-cloud.component';
export * from './components/user-task-cloud/user-task-cloud.component';
export * from './components/user-task-cloud-buttons/user-task-cloud-buttons.component';
export * from './task-form.module';

View File

@@ -16,23 +16,15 @@
*/
import { NgModule } from '@angular/core';
import { FORM_CLOUD_DIRECTIVES } from '../../form/form-cloud.module';
import { TASK_DIRECTIVES } from '../directives/task-directive.module';
import { TaskFormCloudComponent } from './components/task-form-cloud/task-form-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';
/** @deprecated use standalone component imports instead */
export const TASK_FORM_CLOUD_DIRECTIVES = [UserTaskCloudButtonsComponent, TaskFormCloudComponent, UserTaskCloudComponent] as const;
/** @deprecated use standalone component imports instead (...TASK_FORM_CLOUD_DIRECTIVES) */
@NgModule({
imports: [
...FORM_CLOUD_DIRECTIVES,
...TASK_DIRECTIVES,
TaskScreenCloudComponent,
UserTaskCloudButtonsComponent,
TaskFormCloudComponent,
UserTaskCloudComponent
],
exports: [TaskFormCloudComponent, UserTaskCloudComponent]
imports: [...TASK_FORM_CLOUD_DIRECTIVES],
exports: [...TASK_FORM_CLOUD_DIRECTIVES]
})
export class TaskFormModule {}

View File

@@ -33,7 +33,7 @@ import {
TranslationService,
UpdateNotification
} from '@alfresco/adf-core';
import { TaskDetailsCloudModel } from '../../start-task/models/task-details-cloud.model';
import { TaskDetailsCloudModel } from '../../models/task-details-cloud.model';
import { TaskCloudService } from '../../services/task-cloud.service';
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
import { CommonModule } from '@angular/common';

View File

@@ -15,7 +15,7 @@
* limitations under the License.
*/
import { TaskDetailsCloudModel } from '../../start-task/models/task-details-cloud.model';
import { TaskDetailsCloudModel } from '../../models/task-details-cloud.model';
export const taskDetailsWithParentTaskIdMock: TaskDetailsCloudModel = {
appName: 'task-app',
@@ -162,30 +162,6 @@ export const completedTaskDetailsCloudMock: TaskDetailsCloudModel = {
standalone: false
};
export const cancelledTaskDetailsCloudMock: TaskDetailsCloudModel = {
appName: 'mock-app-name',
appVersion: 1,
id: 'mock-task-id',
assignee: 'CancelledTaskAssignee',
name: 'This is a new task',
description: 'This is the description ',
createdDate: new Date(1545048055900),
dueDate: new Date(1545091200000),
claimedDate: null,
priority: 5,
category: null,
processDefinitionId: null,
processInstanceId: null,
status: 'CANCELLED',
owner: 'ownerUser',
parentTaskId: null,
formKey: null,
lastModified: new Date(1545048055900),
lastModifiedTo: null,
lastModifiedFrom: null,
standalone: true
};
export const suspendedTaskDetailsCloudMock: TaskDetailsCloudModel = {
appName: 'mock-app-name',
appVersion: 1,

View File

@@ -43,23 +43,57 @@ import {
UserPreferencesService,
UserPreferenceValues
} from '@alfresco/adf-core';
import { taskPresetsCloudDefaultModel } from '../models/task-preset-cloud.model';
import { TaskQueryCloudRequestModel } from '../../../models/filter-cloud-model';
import { BehaviorSubject, Observable } from 'rxjs';
import { TaskListCloudSortingModel } from '../../../models/task-list-sorting.model';
import { map, take } from 'rxjs/operators';
import { TaskCloudService } from '../../services/task-cloud.service';
import { PreferenceCloudServiceInterface } from '../../../services/preference-cloud.interface';
import { TasksListCloudPreferences } from '../models/tasks-cloud-preferences';
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
// eslint-disable-next-line no-shadow
export enum TasksListCloudPreferences {
columnOrder = 'tasks-list-cloud-columns-order',
columnsVisibility = 'tasks-list-cloud-columns-visibility',
columnsWidths = 'tasks-list-cloud-columns-widths'
}
const taskPresetsCloudDefaultModel = {
default: [
{
id: 'name',
key: 'name',
type: 'text',
title: 'ADF_CLOUD_TASK_LIST.PROPERTIES.NAME',
sortable: true
},
{
id: 'created',
key: 'created',
type: 'text',
title: 'ADF_CLOUD_TASK_LIST.PROPERTIES.CREATED',
cssClass: 'hidden',
sortable: true
},
{
id: 'assignee',
key: 'assignee',
type: 'text',
title: 'ADF_CLOUD_TASK_LIST.PROPERTIES.ASSIGNEE',
cssClass: 'hidden',
sortable: true
}
]
};
/* eslint-disable @typescript-eslint/brace-style */
@Directive()
// eslint-disable-next-line @angular-eslint/directive-class-suffix
export abstract class BaseTaskListCloudComponent<T = unknown>
extends DataTableSchema<T>
// eslint-disable-next-line @typescript-eslint/brace-style
implements OnChanges, AfterContentInit, PaginatedComponent, OnInit {
implements OnChanges, AfterContentInit, PaginatedComponent, OnInit
{
@ContentChild(CustomEmptyContentTemplateDirective)
emptyCustomContent: CustomEmptyContentTemplateDirective;

View File

@@ -29,18 +29,19 @@
>
<adf-loading-content-template>
<ng-template>
<!-- Add your custom loading template here -->
<mat-progress-spinner class="adf-cloud-task-list-loading-margin"
[color]="'primary'"
[mode]="'indeterminate'" />
<mat-progress-spinner
class="adf-cloud-task-list-loading-margin"
[color]="'primary'"
[mode]="'indeterminate'" />
</ng-template>
</adf-loading-content-template>
<adf-no-content-template>
<ng-template>
<adf-empty-content *ngIf="!emptyCustomContent"
icon="assignment"
[title]="'ADF_CLOUD_TASK_LIST.LIST.MESSAGES.TITLE' | translate"
[subtitle]="'ADF_CLOUD_TASK_LIST.LIST.MESSAGES.SUBTITLE' | translate" />
<adf-empty-content
*ngIf="!emptyCustomContent"
icon="assignment"
[title]="'ADF_CLOUD_TASK_LIST.LIST.MESSAGES.TITLE' | translate"
[subtitle]="'ADF_CLOUD_TASK_LIST.LIST.MESSAGES.SUBTITLE' | translate" />
<ng-content select="adf-custom-empty-content-template"></ng-content>
</ng-template>
</adf-no-content-template>

View File

@@ -20,12 +20,12 @@ import { ComponentFixture, TestBed } from '@angular/core/testing';
import { By } from '@angular/platform-browser';
import { AppConfigService, DataRowEvent, ObjectDataRow } from '@alfresco/adf-core';
import { ServiceTaskListCloudComponent } from './service-task-list-cloud.component';
import { fakeServiceTask, fakeCustomSchema } from '../mock/fake-task-response.mock';
import { fakeServiceTask, fakeCustomSchema } from '../../mock/fake-task-response.mock';
import { of } from 'rxjs';
import { ProcessServiceCloudTestingModule } from '../../../testing/process-service-cloud.testing.module';
import { TaskListCloudSortingModel } from '../../../models/task-list-sorting.model';
import { ProcessServiceCloudTestingModule } from '../../../../testing/process-service-cloud.testing.module';
import { TaskListCloudSortingModel } from '../../../../models/task-list-sorting.model';
import { shareReplay, skip } from 'rxjs/operators';
import { ServiceTaskListCloudService } from '../services/service-task-list-cloud.service';
import { ServiceTaskListCloudService } from '../../services/service-task-list-cloud.service';
import { HarnessLoader } from '@angular/cdk/testing';
import { TestbedHarnessEnvironment } from '@angular/cdk/testing/testbed';
import { MatProgressSpinnerHarness } from '@angular/material/progress-spinner/testing';

View File

@@ -16,22 +16,46 @@
*/
import { Component, Inject, Input, ViewEncapsulation } from '@angular/core';
import { AppConfigService, UserPreferencesService } from '@alfresco/adf-core';
import { ServiceTaskQueryCloudRequestModel } from '../models/service-task-cloud.model';
import { BaseTaskListCloudComponent } from './base-task-list-cloud.component';
import { ServiceTaskListCloudService } from '../services/service-task-list-cloud.service';
import { TaskCloudService } from '../../services/task-cloud.service';
import {
AppConfigService,
ColumnsSelectorComponent,
DataTableComponent,
EmptyContentComponent,
LoadingContentTemplateDirective,
MainMenuDataTableTemplateDirective,
NoContentTemplateDirective,
UserPreferencesService
} from '@alfresco/adf-core';
import { ServiceTaskQueryCloudRequestModel } from '../../models/service-task-cloud.model';
import { BaseTaskListCloudComponent } from '../base-task-list-cloud.component';
import { ServiceTaskListCloudService } from '../../services/service-task-list-cloud.service';
import { TaskCloudService } from '../../../services/task-cloud.service';
import { BehaviorSubject, combineLatest } from 'rxjs';
import { PreferenceCloudServiceInterface, TASK_LIST_PREFERENCES_SERVICE_TOKEN } from '../../../services/public-api';
import { PreferenceCloudServiceInterface, TASK_LIST_PREFERENCES_SERVICE_TOKEN } from '../../../../services/public-api';
import { map } from 'rxjs/operators';
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
import { CommonModule } from '@angular/common';
import { TranslateModule } from '@ngx-translate/core';
import { MatProgressSpinnerModule } from '@angular/material/progress-spinner';
const PRESET_KEY = 'adf-cloud-service-task-list.presets';
@Component({
selector: 'adf-cloud-service-task-list',
templateUrl: './base-task-list-cloud.component.html',
styleUrls: ['./base-task-list-cloud.component.scss'],
standalone: true,
imports: [
CommonModule,
ColumnsSelectorComponent,
MainMenuDataTableTemplateDirective,
TranslateModule,
EmptyContentComponent,
NoContentTemplateDirective,
MatProgressSpinnerModule,
LoadingContentTemplateDirective,
DataTableComponent
],
templateUrl: './service-task-list-cloud.component.html',
styleUrls: ['./service-task-list-cloud.component.scss'],
encapsulation: ViewEncapsulation.None
})
export class ServiceTaskListCloudComponent extends BaseTaskListCloudComponent {

Some files were not shown because too many files have changed in this diff Show More