-
-
- @for (group of argumentsFormArray.controls; track group) {
-
- } @empty {
-
{{ 'calculated-fields.no-arguments' | translate }}
+
+
+
+
diff --git a/ui-ngx/src/app/modules/home/components/calculated-fields/components/arguments-table/calculated-field-arguments-table.component.scss b/ui-ngx/src/app/modules/home/components/calculated-fields/components/arguments-table/calculated-field-arguments-table.component.scss
index fe04269150..c4efe323c6 100644
--- a/ui-ngx/src/app/modules/home/components/calculated-fields/components/arguments-table/calculated-field-arguments-table.component.scss
+++ b/ui-ngx/src/app/modules/home/components/calculated-fields/components/arguments-table/calculated-field-arguments-table.component.scss
@@ -13,50 +13,46 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-@use '../../../../../../../scss/constants' as constants;
-
:host {
- .entity-key-field {
- border-bottom: 1px solid rgba(0, 0, 0, 0.12);
- }
+ .arguments-table {
+ min-height: 108px;
- .tb-form-table-row-cell-buttons {
- --mat-badge-legacy-small-size-container-size: 8px;
- --mat-badge-small-size-container-overlap-offset: -5px;
- --mat-badge-small-size-text-size: 0;
- }
+ &-with-error {
+ min-height: 150px;
+ }
- .argument-name-field {
- @media #{constants.$mat-sm} {
- min-width: 25%;
- max-width: 25%;
+ .mat-mdc-table {
+ table-layout: fixed;
}
- @media #{constants.$mat-xs} {
- min-width: 33%;
- max-width: 33%;
+
+ .key-text {
+ font-size: 13px;
}
}
- .datasource-field {
- min-width: 33%;
- max-width: 33%;
+ .tb-form-table-row-cell-buttons {
+ --mat-badge-legacy-small-size-container-size: 8px;
+ --mat-badge-small-size-container-overlap-offset: -5px;
+ --mat-badge-small-size-text-size: 0;
}
}
:host ::ng-deep {
- .entity-field {
- a {
- font-size: 14px;
- white-space: nowrap;
- display: block;
+ .mat-mdc-standard-chip {
+ .mdc-evolution-chip__cell--primary, .mdc-evolution-chip__action--primary, .mdc-evolution-chip__text-label {
overflow: hidden;
- text-overflow: ellipsis;
}
}
- .mat-mdc-standard-chip {
- .mdc-evolution-chip__cell--primary, .mdc-evolution-chip__action--primary, .mdc-evolution-chip__text-label {
- overflow: hidden;
+ .arguments-table:not(.arguments-table-with-error) {
+ .mdc-data-table__row:last-child .mat-mdc-cell {
+ border-bottom: none;
+ }
+ }
+
+ .arguments-table {
+ .mat-mdc-header-row.mat-row-select .mat-mdc-header-cell.entity-type-header {
+ padding: 0 28px 0 0;
}
}
}
diff --git a/ui-ngx/src/app/modules/home/components/calculated-fields/components/arguments-table/calculated-field-arguments-table.component.ts b/ui-ngx/src/app/modules/home/components/calculated-fields/components/arguments-table/calculated-field-arguments-table.component.ts
index 4414c17dd9..2537f34059 100644
--- a/ui-ngx/src/app/modules/home/components/calculated-fields/components/arguments-table/calculated-field-arguments-table.component.ts
+++ b/ui-ngx/src/app/modules/home/components/calculated-fields/components/arguments-table/calculated-field-arguments-table.component.ts
@@ -15,20 +15,22 @@
///
import {
+ AfterViewInit,
ChangeDetectorRef,
Component,
+ DestroyRef,
forwardRef,
Input,
OnChanges,
Renderer2,
SimpleChanges,
+ ViewChild,
ViewContainerRef,
} from '@angular/core';
import {
AbstractControl,
ControlValueAccessor,
FormBuilder,
- FormGroup,
NG_VALIDATORS,
NG_VALUE_ACCESSOR,
ValidationErrors,
@@ -48,8 +50,11 @@ import { TbPopoverService } from '@shared/components/popover.service';
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
import { EntityId } from '@shared/models/id/entity-id';
import { EntityType, entityTypeTranslations } from '@shared/models/entity-type.models';
-import { isDefined, isDefinedAndNotNull } from '@core/utils';
+import { getEntityDetailsPageURL, isDefined, isDefinedAndNotNull } from '@core/utils';
import { TbPopoverComponent } from '@shared/components/popover.component';
+import { TbTableDatasource } from '@shared/components/table/table-datasource.abstract';
+import { EntityService } from '@core/http/entity.service';
+import { MatSort } from '@angular/material/sort';
@Component({
selector: 'tb-calculated-field-arguments-table',
@@ -68,19 +73,23 @@ import { TbPopoverComponent } from '@shared/components/popover.component';
}
],
})
-export class CalculatedFieldArgumentsTableComponent implements ControlValueAccessor, Validator, OnChanges {
+export class CalculatedFieldArgumentsTableComponent implements ControlValueAccessor, Validator, OnChanges, AfterViewInit {
@Input() entityId: EntityId;
@Input() tenantId: string;
@Input() entityName: string;
@Input() calculatedFieldType: CalculatedFieldType;
+ @ViewChild(MatSort, { static: true }) sort: MatSort;
+
errorText = '';
argumentsFormArray = this.fb.array
([]);
+ entityNameMap = new Map();
+ sortOrder = { direction: 'asc', property: '' };
+ dataSource = new CalculatedFieldArgumentDatasource();
readonly entityTypeTranslations = entityTypeTranslations;
readonly ArgumentTypeTranslations = ArgumentTypeTranslations;
- readonly EntityType = EntityType;
readonly ArgumentEntityType = ArgumentEntityType;
readonly ArgumentType = ArgumentType;
readonly CalculatedFieldType = CalculatedFieldType;
@@ -93,10 +102,14 @@ export class CalculatedFieldArgumentsTableComponent implements ControlValueAcces
private popoverService: TbPopoverService,
private viewContainerRef: ViewContainerRef,
private cd: ChangeDetectorRef,
- private renderer: Renderer2
+ private renderer: Renderer2,
+ private entityService: EntityService,
+ private destroyRef: DestroyRef,
) {
- this.argumentsFormArray.valueChanges.pipe(takeUntilDestroyed()).subscribe(() => {
- this.propagateChange(this.getArgumentsObject());
+ this.argumentsFormArray.valueChanges.pipe(takeUntilDestroyed()).subscribe(value => {
+ this.updateEntityNameMap(value);
+ this.updateDataSource(value);
+ this.propagateChange(this.getArgumentsObject(value));
});
}
@@ -107,6 +120,14 @@ export class CalculatedFieldArgumentsTableComponent implements ControlValueAcces
}
}
+ ngAfterViewInit(): void {
+ this.sort.sortChange.asObservable().pipe(takeUntilDestroyed(this.destroyRef)).subscribe(() => {
+ this.sortOrder.property = this.sort.active;
+ this.sortOrder.direction = this.sort.direction;
+ this.updateDataSource(this.argumentsFormArray.value);
+ });
+ }
+
registerOnChange(fn: (argumentsObj: Record) => void): void {
this.propagateChange = fn;
}
@@ -118,12 +139,13 @@ export class CalculatedFieldArgumentsTableComponent implements ControlValueAcces
return this.errorText ? { argumentsFormArray: false } : null;
}
- onDelete(index: number): void {
+ onDelete($event: Event, index: number): void {
+ $event.stopPropagation();
this.argumentsFormArray.removeAt(index);
this.argumentsFormArray.markAsDirty();
}
- manageArgument($event: Event, matButton: MatButton, index?: number): void {
+ manageArgument($event: Event, matButton: MatButton, argument = {} as CalculatedFieldArgumentValue, index?: number): void {
$event?.stopPropagation();
if (this.popoverComponent && !this.popoverComponent.tbHidden) {
this.popoverComponent.hide();
@@ -132,16 +154,15 @@ export class CalculatedFieldArgumentsTableComponent implements ControlValueAcces
if (this.popoverService.hasPopover(trigger)) {
this.popoverService.hidePopover(trigger);
} else {
- const argumentObj = this.argumentsFormArray.at(index)?.getRawValue() ?? {};
const ctx = {
index,
- argument: argumentObj,
+ argument,
entityId: this.entityId,
calculatedFieldType: this.calculatedFieldType,
buttonTitle: this.argumentsFormArray.at(index)?.value ? 'action.apply' : 'action.add',
tenantId: this.tenantId,
entityName: this.entityName,
- usedArgumentNames: this.argumentsFormArray.getRawValue().map(({ argumentName }) => argumentName).filter(name => name !== argumentObj.argumentName),
+ usedArgumentNames: this.argumentsFormArray.value.map(({ argumentName }) => argumentName).filter(name => name !== argument.argumentName),
};
this.popoverComponent = this.popoverService.displayPopover(trigger, this.renderer,
this.viewContainerRef, CalculatedFieldArgumentPanelComponent, isDefined(index) ? 'left' : 'right', false, null,
@@ -150,7 +171,7 @@ export class CalculatedFieldArgumentsTableComponent implements ControlValueAcces
{}, {}, true);
this.popoverComponent.tbComponentRef.instance.argumentsDataApplied.subscribe(({ value, index }) => {
this.popoverComponent.hide();
- const formGroup = this.getArgumentFormGroup(value);
+ const formGroup = this.fb.group(value);
if (isDefinedAndNotNull(index)) {
this.argumentsFormArray.setControl(index, formGroup);
} else {
@@ -162,9 +183,14 @@ export class CalculatedFieldArgumentsTableComponent implements ControlValueAcces
}
}
+ private updateDataSource(value: CalculatedFieldArgumentValue[]): void {
+ const sortedValue = this.sortData(value);
+ this.dataSource.loadData(sortedValue);
+ }
+
private updateErrorText(): void {
if (this.calculatedFieldType === CalculatedFieldType.SIMPLE
- && this.argumentsFormArray.controls.some(control => control.get('refEntityKey').get('type').value === ArgumentType.Rolling)) {
+ && this.argumentsFormArray.controls.some(control => control.value.refEntityKey.type === ArgumentType.Rolling)) {
this.errorText = 'calculated-fields.hint.arguments-simple-with-rolling';
} else if (!this.argumentsFormArray.controls.length) {
this.errorText = 'calculated-fields.hint.arguments-empty';
@@ -173,9 +199,9 @@ export class CalculatedFieldArgumentsTableComponent implements ControlValueAcces
}
}
- private getArgumentsObject(): Record {
- return this.argumentsFormArray.getRawValue().reduce((acc, rawValue) => {
- const { argumentName, ...argument } = rawValue as CalculatedFieldArgumentValue;
+ private getArgumentsObject(value: CalculatedFieldArgumentValue[]): Record {
+ return value.reduce((acc, argumentValue) => {
+ const { argumentName, ...argument } = argumentValue as CalculatedFieldArgumentValue;
acc[argumentName] = argument;
return acc;
}, {} as Record);
@@ -186,31 +212,62 @@ export class CalculatedFieldArgumentsTableComponent implements ControlValueAcces
this.populateArgumentsFormArray(argumentsObj)
}
+ getEntityDetailsPageURL(id: string, type: EntityType): string {
+ return getEntityDetailsPageURL(id, type);
+ }
+
private populateArgumentsFormArray(argumentsObj: Record): void {
Object.keys(argumentsObj).forEach(key => {
const value: CalculatedFieldArgumentValue = {
...argumentsObj[key],
argumentName: key
};
- this.argumentsFormArray.push(this.getArgumentFormGroup(value), {emitEvent: false});
+ this.argumentsFormArray.push(this.fb.group(value), { emitEvent: false });
+ });
+ this.argumentsFormArray.updateValueAndValidity();
+ }
+
+ private updateEntityNameMap(value: CalculatedFieldArgumentValue[]): void {
+ value.forEach(({ refEntityId = {}}) => {
+ if (refEntityId.id && !this.entityNameMap.has(refEntityId.id)) {
+ const { id, entityType } = refEntityId as EntityId;
+ this.entityService.getEntity(entityType as EntityType, id, { ignoreLoading: true, ignoreErrors: true })
+ .pipe(takeUntilDestroyed(this.destroyRef))
+ .subscribe(entity => this.entityNameMap.set(id, entity.name));
+ }
+ });
+ }
+
+ private getSortValue(argument: CalculatedFieldArgumentValue, column: string): string {
+ switch (column) {
+ case 'entityType':
+ if (argument.refEntityId?.entityType === ArgumentEntityType.Tenant) {
+ return 'calculated-fields.argument-current-tenant';
+ } else if (argument.refEntityId?.id) {
+ return entityTypeTranslations.get((argument.refEntityId)?.entityType as unknown as EntityType).type;
+ } else {
+ return 'calculated-fields.argument-current';
+ }
+ case 'type':
+ return ArgumentTypeTranslations.get(argument.refEntityKey.type);
+ case 'key':
+ return argument.refEntityKey.key;
+ default:
+ return argument.argumentName;
+ }
+ }
+
+ private sortData(data: CalculatedFieldArgumentValue[]): CalculatedFieldArgumentValue[] {
+ return data.sort((a, b) => {
+ const valA = this.getSortValue(a, this.sortOrder.property) ?? '';
+ const valB = this.getSortValue(b, this.sortOrder.property) ?? '';
+ return (this.sortOrder.direction === 'asc' ? 1 : -1) * valA.localeCompare(valB);
});
}
+}
- private getArgumentFormGroup(value: CalculatedFieldArgumentValue): FormGroup {
- return this.fb.group({
- ...value,
- argumentName: [{ value: value.argumentName, disabled: true }],
- ...(value.refEntityId ? {
- refEntityId: this.fb.group({
- entityType: [{ value: value.refEntityId.entityType, disabled: true }],
- id: [{ value: value.refEntityId.id , disabled: true }],
- }),
- } : {}),
- refEntityKey: this.fb.group({
- ...value.refEntityKey,
- type: [{ value: value.refEntityKey.type, disabled: true }],
- key: [{ value: value.refEntityKey.key, disabled: true }],
- }),
- })
+class CalculatedFieldArgumentDatasource extends TbTableDatasource {
+ constructor() {
+ super();
}
}
diff --git a/ui-ngx/src/app/modules/home/components/calculated-fields/components/dialog/calculated-field-dialog.component.ts b/ui-ngx/src/app/modules/home/components/calculated-fields/components/dialog/calculated-field-dialog.component.ts
index 63e684bf6e..c3e3d52a72 100644
--- a/ui-ngx/src/app/modules/home/components/calculated-fields/components/dialog/calculated-field-dialog.component.ts
+++ b/ui-ngx/src/app/modules/home/components/calculated-fields/components/dialog/calculated-field-dialog.component.ts
@@ -143,10 +143,10 @@ export class CalculatedFieldDialogComponent extends DialogComponent, 'label'>, HasVersion, HasTenantId, ExportableEntity {
- debugSettings?: EntityDebugSettings;
+export interface CalculatedField extends Omit, 'label'>, HasVersion, HasEntityDebugSettings, HasTenantId, ExportableEntity {
configuration: CalculatedFieldConfiguration;
type: CalculatedFieldType;
entityId: EntityId;