Optimized entities map

This commit is contained in:
mpetrov 2025-03-27 11:16:06 +02:00
parent 6888f9ed5b
commit f74ca2fb97
4 changed files with 57 additions and 24 deletions

View File

@ -49,7 +49,7 @@ import { TbPopoverService } from '@shared/components/popover.service';
import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
import { EntityId } from '@shared/models/id/entity-id'; import { EntityId } from '@shared/models/id/entity-id';
import { EntityType, entityTypeTranslations } from '@shared/models/entity-type.models'; import { EntityType, entityTypeTranslations } from '@shared/models/entity-type.models';
import { getEntityDetailsPageURL, isDefined, isDefinedAndNotNull, isEqual } from '@core/utils'; import { getEntityDetailsPageURL, isEqual } from '@core/utils';
import { TbPopoverComponent } from '@shared/components/popover.component'; import { TbPopoverComponent } from '@shared/components/popover.component';
import { TbTableDatasource } from '@shared/components/table/table-datasource.abstract'; import { TbTableDatasource } from '@shared/components/table/table-datasource.abstract';
import { EntityService } from '@core/http/entity.service'; import { EntityService } from '@core/http/entity.service';
@ -57,9 +57,9 @@ import { MatSort } from '@angular/material/sort';
import { getCurrentAuthState } from '@core/auth/auth.selectors'; import { getCurrentAuthState } from '@core/auth/auth.selectors';
import { Store } from '@ngrx/store'; import { Store } from '@ngrx/store';
import { AppState } from '@core/core.state'; import { AppState } from '@core/core.state';
import { catchError } from 'rxjs/operators'; import { forkJoin, Observable } from 'rxjs';
import { NEVER } from 'rxjs';
import { NULL_UUID } from '@shared/models/id/has-uuid'; import { NULL_UUID } from '@shared/models/id/has-uuid';
import { BaseData } from '@shared/models/base-data';
@Component({ @Component({
selector: 'tb-calculated-field-arguments-table', selector: 'tb-calculated-field-arguments-table',
@ -115,7 +115,6 @@ export class CalculatedFieldArgumentsTableComponent implements ControlValueAcces
private store: Store<AppState> private store: Store<AppState>
) { ) {
this.argumentsFormArray.valueChanges.pipe(takeUntilDestroyed()).subscribe(value => { this.argumentsFormArray.valueChanges.pipe(takeUntilDestroyed()).subscribe(value => {
this.updateEntityNameMap(value);
this.updateDataSource(value); this.updateDataSource(value);
this.propagateChange(this.getArgumentsObject(value)); this.propagateChange(this.getArgumentsObject(value));
}); });
@ -180,8 +179,11 @@ export class CalculatedFieldArgumentsTableComponent implements ControlValueAcces
ctx, ctx,
{}, {},
{}, {}, true); {}, {}, true);
this.popoverComponent.tbComponentRef.instance.argumentsDataApplied.subscribe(value=> { this.popoverComponent.tbComponentRef.instance.argumentsDataApplied.subscribe(({ entityName, ...value }) => {
this.popoverComponent.hide(); this.popoverComponent.hide();
if (entityName) {
this.entityNameMap.set(value.refEntityId.id, entityName);
}
if (isExists) { if (isExists) {
this.argumentsFormArray.at(index).setValue(value); this.argumentsFormArray.at(index).setValue(value);
} else { } else {
@ -220,7 +222,8 @@ export class CalculatedFieldArgumentsTableComponent implements ControlValueAcces
writeValue(argumentsObj: Record<string, CalculatedFieldArgument>): void { writeValue(argumentsObj: Record<string, CalculatedFieldArgument>): void {
this.argumentsFormArray.clear(); this.argumentsFormArray.clear();
this.populateArgumentsFormArray(argumentsObj) this.populateArgumentsFormArray(argumentsObj);
this.updateEntityNameMap(this.argumentsFormArray.value);
} }
getEntityDetailsPageURL(id: string, type: EntityType): string { getEntityDetailsPageURL(id: string, type: EntityType): string {
@ -238,21 +241,41 @@ export class CalculatedFieldArgumentsTableComponent implements ControlValueAcces
this.argumentsFormArray.updateValueAndValidity(); this.argumentsFormArray.updateValueAndValidity();
} }
private updateEntityNameMap(value: CalculatedFieldArgumentValue[]): void { private updateEntityNameMap(values: CalculatedFieldArgumentValue[]): void {
value.forEach(({ refEntityId = {}}) => { const entitiesByType = values.reduce((acc, { refEntityId = {}}) => {
if (refEntityId.id && !this.entityNameMap.has(refEntityId.id)) { if (refEntityId.id && refEntityId.entityType !== ArgumentEntityType.Tenant) {
const { id, entityType } = refEntityId as EntityId; const { id, entityType } = refEntityId as EntityId;
this.entityService.getEntity(entityType as EntityType, id, { ignoreLoading: true, ignoreErrors: true }) acc[entityType] = acc[entityType] ?? [];
.pipe( acc[entityType].push(id);
catchError(() => { }
const control = this.argumentsFormArray.controls.find(control => control.value.refEntityId?.id === id); return acc;
control.setValue({ ...control.value, refEntityId: { ...control.value.refEntityId, id: NULL_UUID } }); }, {} as Record<EntityType, string[]>);
const tasks = Object.entries(entitiesByType).map(([entityType, ids]) =>
this.entityService.getEntities(entityType as EntityType, ids)
);
if (!tasks.length) {
return;
}
this.fetchEntityNames(tasks, values);
}
return NEVER; private fetchEntityNames(tasks: Observable<BaseData<EntityId>[]>[], values: CalculatedFieldArgumentValue[]): void {
}), forkJoin(tasks as Observable<BaseData<EntityId>[]>[])
takeUntilDestroyed(this.destroyRef) .pipe(takeUntilDestroyed(this.destroyRef))
) .subscribe((result: Array<BaseData<EntityId>>[]) => {
.subscribe(entity => this.entityNameMap.set(id, entity.name)); result.forEach((entities: BaseData<EntityId>[]) => entities.forEach((entity: BaseData<EntityId>) => this.entityNameMap.set(entity.id.id, entity.name)));
let updateTable = false;
values.forEach(({ refEntityId }) => {
if (refEntityId?.id && !this.entityNameMap.has(refEntityId.id) && refEntityId.entityType !== ArgumentEntityType.Tenant) {
updateTable = true;
const control = this.argumentsFormArray.controls.find(control => control.value.refEntityId?.id === refEntityId.id);
const value = control.value;
value.refEntityId.id = NULL_UUID;
control.setValue(value, { emitEvent: false });
}
});
if (updateTable) {
this.argumentsFormArray.updateValueAndValidity();
} }
}); });
} }

View File

@ -88,6 +88,7 @@
[placeholder]="'action.set' | translate" [placeholder]="'action.set' | translate"
[required]="true" [required]="true"
[entityType]="ArgumentEntityTypeParamsMap.get(entityType).entityType" [entityType]="ArgumentEntityTypeParamsMap.get(entityType).entityType"
(entityChanged)="entityNameSubject.next($event?.name)"
/> />
</div> </div>
} }

View File

@ -36,7 +36,7 @@ import { EntityId } from '@shared/models/id/entity-id';
import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
import { EntityFilter } from '@shared/models/query/query.models'; import { EntityFilter } from '@shared/models/query/query.models';
import { AliasFilterType } from '@shared/models/alias.models'; import { AliasFilterType } from '@shared/models/alias.models';
import { merge } from 'rxjs'; import { BehaviorSubject, merge } from 'rxjs';
import { MINUTE } from '@shared/models/time/time.models'; import { MINUTE } from '@shared/models/time/time.models';
import { getCurrentAuthState } from '@core/auth/auth.selectors'; import { getCurrentAuthState } from '@core/auth/auth.selectors';
import { AppState } from '@core/core.state'; import { AppState } from '@core/core.state';
@ -84,6 +84,7 @@ export class CalculatedFieldArgumentPanelComponent implements OnInit, AfterViewI
argumentTypes: ArgumentType[]; argumentTypes: ArgumentType[];
entityFilter: EntityFilter; entityFilter: EntityFilter;
entityNameSubject = new BehaviorSubject<string>(null);
readonly argumentEntityTypes = Object.values(ArgumentEntityType) as ArgumentEntityType[]; readonly argumentEntityTypes = Object.values(ArgumentEntityType) as ArgumentEntityType[];
readonly ArgumentEntityTypeTranslations = ArgumentEntityTypeTranslations; readonly ArgumentEntityTypeTranslations = ArgumentEntityTypeTranslations;
@ -151,6 +152,9 @@ export class CalculatedFieldArgumentPanelComponent implements OnInit, AfterViewI
if (refEntityId.entityType === ArgumentEntityType.Tenant) { if (refEntityId.entityType === ArgumentEntityType.Tenant) {
refEntityId.id = this.tenantId; refEntityId.id = this.tenantId;
} }
if (refEntityId.entityType !== ArgumentEntityType.Current && refEntityId.entityType !== ArgumentEntityType.Tenant) {
value.entityName = this.entityNameSubject.value;
}
if (value.defaultValue) { if (value.defaultValue) {
value.defaultValue = value.defaultValue.trim(); value.defaultValue = value.defaultValue.trim();
} }
@ -211,12 +215,16 @@ export class CalculatedFieldArgumentPanelComponent implements OnInit, AfterViewI
} }
private observeEntityTypeChanges(): void { private observeEntityTypeChanges(): void {
this.argumentFormGroup.get('refEntityId').get('entityType').valueChanges this.refEntityIdFormGroup.get('entityType').valueChanges
.pipe(distinctUntilChanged(), takeUntilDestroyed()) .pipe(distinctUntilChanged(), takeUntilDestroyed())
.subscribe(type => { .subscribe(type => {
this.argumentFormGroup.get('refEntityId').get('id').setValue(''); this.argumentFormGroup.get('refEntityId').get('id').setValue('');
const isEntityWithId = type !== ArgumentEntityType.Tenant && type !== ArgumentEntityType.Current;
this.argumentFormGroup.get('refEntityId') this.argumentFormGroup.get('refEntityId')
.get('id')[type === ArgumentEntityType.Tenant || type === ArgumentEntityType.Current ? 'disable' : 'enable'](); .get('id')[isEntityWithId ? 'enable' : 'disable']();
if (!isEntityWithId) {
this.entityNameSubject.next(null);
}
if (!this.enableAttributeScopeSelection) { if (!this.enableAttributeScopeSelection) {
this.refEntityKeyFormGroup.get('scope').setValue(AttributeScope.SERVER_SCOPE); this.refEntityKeyFormGroup.get('scope').setValue(AttributeScope.SERVER_SCOPE);
} }

View File

@ -144,6 +144,7 @@ export interface RefEntityId {
export interface CalculatedFieldArgumentValue extends CalculatedFieldArgument { export interface CalculatedFieldArgumentValue extends CalculatedFieldArgument {
argumentName: string; argumentName: string;
entityName?: string;
} }
export type CalculatedFieldTestScriptFn = (calculatedField: CalculatedField, argumentsObj?: Record<string, unknown>, closeAllOnSave?: boolean) => Observable<string>; export type CalculatedFieldTestScriptFn = (calculatedField: CalculatedField, argumentsObj?: Record<string, unknown>, closeAllOnSave?: boolean) => Observable<string>;