Merge pull request #12743 from maxunbearable/feature/calculated-fields-adjustments-24-02
Calculated fields adjustments
This commit is contained in:
		
						commit
						bd1e67a185
					
				@ -16,80 +16,78 @@
 | 
			
		||||
 | 
			
		||||
-->
 | 
			
		||||
<div class="flex flex-col gap-3">
 | 
			
		||||
    <div class="tb-form-table">
 | 
			
		||||
        <div class="tb-form-table-header">
 | 
			
		||||
            <div class="argument-name-field tb-form-table-header-cell w-1/6 xs:w-1/3 sm:w-1/4" tbTruncateWithTooltip>{{ 'calculated-fields.argument-name' | translate }}</div>
 | 
			
		||||
            <div class="tb-form-table-header-cell w-1/3 xs:hidden">{{ 'calculated-fields.datasource' | translate }}</div>
 | 
			
		||||
            <div class="tb-form-table-header-cell w-1/6 lt-md:hidden">{{ 'common.type' | translate }}</div>
 | 
			
		||||
            <div class="tb-form-table-header-cell w-1/6 xs:w-1/3">{{ 'entity.key' | translate }}</div>
 | 
			
		||||
            <div class="tb-form-table-header-cell w-20 min-w-20"></div>
 | 
			
		||||
        </div>
 | 
			
		||||
        <div class="tb-form-table-body">
 | 
			
		||||
            @for (group of argumentsFormArray.controls; track group) {
 | 
			
		||||
                <div [formGroup]="group" class="tb-form-table-row">
 | 
			
		||||
                    <mat-form-field appearance="outline" class="argument-name-field tb-inline-field w-1/6 xs:w-1/3 sm:w-1/4" subscriptSizing="dynamic">
 | 
			
		||||
                        <input matInput formControlName="argumentName" placeholder="{{ 'action.set' | translate }}">
 | 
			
		||||
                    </mat-form-field>
 | 
			
		||||
                    <section class="datasource-field flex w-1/3 gap-2 xs:hidden">
 | 
			
		||||
                        @if (group.get('refEntityId')?.get('id')?.value) {
 | 
			
		||||
                            <ng-container [formGroup]="group.get('refEntityId')">
 | 
			
		||||
                                <mat-form-field appearance="outline" class="tb-inline-field w-1/2" subscriptSizing="dynamic">
 | 
			
		||||
                                    <mat-select [value]="group.get('refEntityId').get('entityType').value" formControlName="entityType">
 | 
			
		||||
                                        <mat-option [value]="group.get('refEntityId').get('entityType').value">
 | 
			
		||||
                                            {{ entityTypeTranslations.get(group.get('refEntityId').get('entityType').value)?.type | translate }}
 | 
			
		||||
                                        </mat-option>
 | 
			
		||||
                                    </mat-select>
 | 
			
		||||
                                </mat-form-field>
 | 
			
		||||
                                <tb-entity-autocomplete
 | 
			
		||||
                                        class="entity-field w-1/2"
 | 
			
		||||
                                        formControlName="id"
 | 
			
		||||
                                        [inlineField]="true"
 | 
			
		||||
                                        [hideLabel]="true"
 | 
			
		||||
                                        [placeholder]="'action.set' | translate"
 | 
			
		||||
                                        [entityType]="group.get('refEntityId').get('entityType').value"
 | 
			
		||||
                                />
 | 
			
		||||
  <div class="tb-form-panel stroked no-padding no-gap arguments-table flex flex-col" [class.arguments-table-with-error]="errorText">
 | 
			
		||||
    <table mat-table [dataSource]="dataSource" class="overflow-hidden bg-transparent" matSort
 | 
			
		||||
           [matSortActive]="sortOrder.property" [matSortDirection]="sortOrder.direction" matSortDisableClear>
 | 
			
		||||
      <ng-container [matColumnDef]="'name'">
 | 
			
		||||
        <mat-header-cell mat-sort-header *matHeaderCellDef class="!w-1/4 xs:!w-1/2">
 | 
			
		||||
          <div tbTruncateWithTooltip>{{ 'common.name' | translate }}</div>
 | 
			
		||||
        </mat-header-cell>
 | 
			
		||||
        <mat-cell *matCellDef="let argument" class="w-1/4 xs:w-1/2">
 | 
			
		||||
          <div tbTruncateWithTooltip>{{ argument.argumentName }}</div>
 | 
			
		||||
        </mat-cell>
 | 
			
		||||
      </ng-container>
 | 
			
		||||
                        } @else {
 | 
			
		||||
                            <mat-form-field appearance="outline" class="tb-inline-field flex-1" subscriptSizing="dynamic">
 | 
			
		||||
                                <mat-select [value]="'current'" [disabled]="true">
 | 
			
		||||
                                    <mat-option [value]="'current'">
 | 
			
		||||
                                        {{
 | 
			
		||||
                                            (group.get('refEntityId')?.get('entityType')?.value === ArgumentEntityType.Tenant
 | 
			
		||||
                                                ? 'calculated-fields.argument-current-tenant'
 | 
			
		||||
                                                : 'calculated-fields.argument-current') | translate
 | 
			
		||||
                                        }}
 | 
			
		||||
                                    </mat-option>
 | 
			
		||||
                                </mat-select>
 | 
			
		||||
                            </mat-form-field>
 | 
			
		||||
                        }
 | 
			
		||||
                    </section>
 | 
			
		||||
                    <ng-container [formGroup]="group.get('refEntityKey')">
 | 
			
		||||
                        <mat-form-field appearance="outline" class="tb-inline-field w-1/6 lt-md:hidden" subscriptSizing="dynamic">
 | 
			
		||||
                            @if (group.get('refEntityKey').get('type').value; as type) {
 | 
			
		||||
                                <mat-select [value]="type" formControlName="type">
 | 
			
		||||
                                    <mat-option [value]="type">
 | 
			
		||||
                                        {{ ArgumentTypeTranslations.get(type) | translate }}
 | 
			
		||||
                                    </mat-option>
 | 
			
		||||
                                </mat-select>
 | 
			
		||||
                            }
 | 
			
		||||
                        </mat-form-field>
 | 
			
		||||
                        <mat-chip-listbox formControlName="key" class="tb-inline-field entity-key-field w-1/6 xs:w-1/3">
 | 
			
		||||
                            <mat-chip>
 | 
			
		||||
      <ng-container [matColumnDef]="'entityType'">
 | 
			
		||||
        <mat-header-cell mat-sort-header *matHeaderCellDef class="entity-type-header w-1/4 xs:hidden">
 | 
			
		||||
          {{ 'entity.entity-type' | translate }}
 | 
			
		||||
        </mat-header-cell>
 | 
			
		||||
        <mat-cell *matCellDef="let argument" class="w-1/4 xs:hidden">
 | 
			
		||||
          <div tbTruncateWithTooltip>
 | 
			
		||||
                                    {{ group.get('refEntityKey').get('key').value }}
 | 
			
		||||
            @if (argument.refEntityId?.entityType === ArgumentEntityType.Tenant) {
 | 
			
		||||
              {{ 'calculated-fields.argument-current-tenant' | translate }}
 | 
			
		||||
            } @else if (argument.refEntityId?.id) {
 | 
			
		||||
              {{ entityTypeTranslations.get(argument.refEntityId.entityType).type | translate }}
 | 
			
		||||
            } @else {
 | 
			
		||||
              {{ 'calculated-fields.argument-current' | translate }}
 | 
			
		||||
            }
 | 
			
		||||
          </div>
 | 
			
		||||
                            </mat-chip>
 | 
			
		||||
                        </mat-chip-listbox>
 | 
			
		||||
        </mat-cell>
 | 
			
		||||
      </ng-container>
 | 
			
		||||
      <ng-container [matColumnDef]="'target'">
 | 
			
		||||
        <mat-header-cell *matHeaderCellDef class="w-1/4 xs:hidden">
 | 
			
		||||
          {{ 'entity-view.target-entity' | translate }}
 | 
			
		||||
        </mat-header-cell>
 | 
			
		||||
        <mat-cell *matCellDef="let argument" class="w-1/4 xs:hidden">
 | 
			
		||||
          <div tbTruncateWithTooltip>
 | 
			
		||||
            @if (argument.refEntityId?.id) {
 | 
			
		||||
              <a aria-label="Open entity details page"
 | 
			
		||||
                 [routerLink]="getEntityDetailsPageURL(argument.refEntityId.id, argument.refEntityId.entityType)">
 | 
			
		||||
                {{ entityNameMap.get(argument.refEntityId.id) ?? '' }}
 | 
			
		||||
              </a>
 | 
			
		||||
            }
 | 
			
		||||
          </div>
 | 
			
		||||
        </mat-cell>
 | 
			
		||||
      </ng-container>
 | 
			
		||||
      <ng-container [matColumnDef]="'type'">
 | 
			
		||||
        <mat-header-cell mat-sort-header *matHeaderCellDef class="w-1/4 lt-md:hidden">
 | 
			
		||||
          {{ 'common.type' | translate }}
 | 
			
		||||
        </mat-header-cell>
 | 
			
		||||
        <mat-cell *matCellDef="let argument" class="w-1/4 lt-md:hidden">
 | 
			
		||||
          <div tbTruncateWithTooltip>{{ ArgumentTypeTranslations.get(argument.refEntityKey.type) | translate }}</div>
 | 
			
		||||
        </mat-cell>
 | 
			
		||||
      </ng-container>
 | 
			
		||||
      <ng-container [matColumnDef]="'key'">
 | 
			
		||||
        <mat-header-cell mat-sort-header *matHeaderCellDef class="w-1/4 xs:w-1/3">
 | 
			
		||||
          {{ 'entity.key' | translate }}
 | 
			
		||||
        </mat-header-cell>
 | 
			
		||||
        <mat-cell *matCellDef="let argument" class="w-1/4 xs:w-1/3">
 | 
			
		||||
          <mat-chip>
 | 
			
		||||
            <div tbTruncateWithTooltip class="key-text">{{ argument.refEntityKey.key }}</div>
 | 
			
		||||
          </mat-chip>
 | 
			
		||||
        </mat-cell>
 | 
			
		||||
      </ng-container>
 | 
			
		||||
      <ng-container matColumnDef="actions" stickyEnd>
 | 
			
		||||
        <mat-header-cell *matHeaderCellDef class="w-20 min-w-20"/>
 | 
			
		||||
        <mat-cell *matCellDef="let argument; let $index = index">
 | 
			
		||||
          <div class="tb-form-table-row-cell-buttons flex w-20 min-w-20">
 | 
			
		||||
            <button type="button"
 | 
			
		||||
                    mat-icon-button
 | 
			
		||||
                    #button
 | 
			
		||||
                                (click)="manageArgument($event, button, $index)"
 | 
			
		||||
                    (click)="manageArgument($event, button, argument, $index)"
 | 
			
		||||
                    [matTooltip]="'action.edit' | translate"
 | 
			
		||||
                    matTooltipPosition="above">
 | 
			
		||||
              <mat-icon
 | 
			
		||||
                              [matBadgeHidden]="!(group.get('refEntityKey').get('type').value === ArgumentType.Rolling
 | 
			
		||||
                [matBadgeHidden]="!(argument.refEntityKey.type === ArgumentType.Rolling
 | 
			
		||||
                          && calculatedFieldType === CalculatedFieldType.SIMPLE)"
 | 
			
		||||
                matBadgeColor="warn"
 | 
			
		||||
                matBadgeSize="small"
 | 
			
		||||
@ -100,19 +98,25 @@
 | 
			
		||||
            </button>
 | 
			
		||||
            <button type="button"
 | 
			
		||||
                    mat-icon-button
 | 
			
		||||
                                (click)="onDelete($index)"
 | 
			
		||||
                    (click)="onDelete($event, $index)"
 | 
			
		||||
                    [matTooltip]="'action.delete' | translate"
 | 
			
		||||
                    matTooltipPosition="above">
 | 
			
		||||
              <mat-icon>delete</mat-icon>
 | 
			
		||||
            </button>
 | 
			
		||||
          </div>
 | 
			
		||||
                </div>
 | 
			
		||||
            } @empty {
 | 
			
		||||
                <span class="tb-prompt flex items-center justify-center">{{ 'calculated-fields.no-arguments' | translate }}</span>
 | 
			
		||||
            }
 | 
			
		||||
        </mat-cell>
 | 
			
		||||
      </ng-container>
 | 
			
		||||
      <mat-header-row class="mat-row-select"
 | 
			
		||||
                      *matHeaderRowDef="['name', 'entityType', 'target', 'type', 'key', 'actions']; sticky: true"></mat-header-row>
 | 
			
		||||
      <mat-row
 | 
			
		||||
        *matRowDef="let argument; columns: ['name', 'entityType', 'target', 'type', 'key', 'actions']"></mat-row>
 | 
			
		||||
    </table>
 | 
			
		||||
    <div [class.!hidden]="(dataSource.isEmpty() | async) === false"
 | 
			
		||||
         class="tb-prompt flex flex-1 items-end justify-center">
 | 
			
		||||
      {{ 'calculated-fields.no-arguments' | translate }}
 | 
			
		||||
    </div>
 | 
			
		||||
    @if (errorText) {
 | 
			
		||||
            <tb-error noMargin [error]="errorText | translate" class="pl-3"/>
 | 
			
		||||
      <tb-error noMargin [error]="errorText | translate" class="flex h-9 items-center pl-3"/>
 | 
			
		||||
    }
 | 
			
		||||
  </div>
 | 
			
		||||
  <div>
 | 
			
		||||
 | 
			
		||||
@ -13,11 +13,21 @@
 | 
			
		||||
 * 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;
 | 
			
		||||
 | 
			
		||||
    &-with-error {
 | 
			
		||||
      min-height: 150px;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    .mat-mdc-table {
 | 
			
		||||
      table-layout: fixed;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    .key-text {
 | 
			
		||||
      font-size: 13px;
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  .tb-form-table-row-cell-buttons {
 | 
			
		||||
@ -25,38 +35,24 @@
 | 
			
		||||
    --mat-badge-small-size-container-overlap-offset: -5px;
 | 
			
		||||
    --mat-badge-small-size-text-size: 0;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  .argument-name-field {
 | 
			
		||||
    @media #{constants.$mat-sm} {
 | 
			
		||||
      min-width: 25%;
 | 
			
		||||
      max-width: 25%;
 | 
			
		||||
    }
 | 
			
		||||
    @media #{constants.$mat-xs} {
 | 
			
		||||
      min-width: 33%;
 | 
			
		||||
      max-width: 33%;
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  .datasource-field {
 | 
			
		||||
    min-width: 33%;
 | 
			
		||||
    max-width: 33%;
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
:host ::ng-deep {
 | 
			
		||||
  .entity-field {
 | 
			
		||||
    a {
 | 
			
		||||
      font-size: 14px;
 | 
			
		||||
      white-space: nowrap;
 | 
			
		||||
      display: block;
 | 
			
		||||
      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;
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -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<AbstractControl>([]);
 | 
			
		||||
  entityNameMap = new Map<string, string>();
 | 
			
		||||
  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<string, CalculatedFieldArgument>) => 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<string, CalculatedFieldArgument> {
 | 
			
		||||
    return this.argumentsFormArray.getRawValue().reduce((acc, rawValue) => {
 | 
			
		||||
      const { argumentName, ...argument } = rawValue as CalculatedFieldArgumentValue;
 | 
			
		||||
  private getArgumentsObject(value: CalculatedFieldArgumentValue[]): Record<string, CalculatedFieldArgument> {
 | 
			
		||||
    return value.reduce((acc, argumentValue) => {
 | 
			
		||||
      const { argumentName, ...argument } = argumentValue as CalculatedFieldArgumentValue;
 | 
			
		||||
      acc[argumentName] = argument;
 | 
			
		||||
      return acc;
 | 
			
		||||
    }, {} as Record<string, CalculatedFieldArgument>);
 | 
			
		||||
@ -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<string, CalculatedFieldArgument>): 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 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 }],
 | 
			
		||||
      }),
 | 
			
		||||
    })
 | 
			
		||||
  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);
 | 
			
		||||
    });
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
class CalculatedFieldArgumentDatasource extends TbTableDatasource<CalculatedFieldArgumentValue> {
 | 
			
		||||
  constructor() {
 | 
			
		||||
    super();
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -143,10 +143,10 @@ export class CalculatedFieldDialogComponent extends DialogComponent<CalculatedFi
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  private applyDialogData(): void {
 | 
			
		||||
    const { configuration = {}, type = CalculatedFieldType.SIMPLE, ...value } = this.data.value ?? {};
 | 
			
		||||
    const { configuration = {}, type = CalculatedFieldType.SIMPLE, debugSettings = { failuresEnabled: true, allEnabled: true }, ...value } = this.data.value ?? {};
 | 
			
		||||
    const { expression, ...restConfig } = configuration as CalculatedFieldConfiguration;
 | 
			
		||||
    const updatedConfig = { ...restConfig , ['expression'+type]: expression };
 | 
			
		||||
    this.fieldFormGroup.patchValue({ configuration: updatedConfig, type, ...value }, {emitEvent: false});
 | 
			
		||||
    this.fieldFormGroup.patchValue({ configuration: updatedConfig, type, debugSettings, ...value }, {emitEvent: false});
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  private observeTypeChanges(): void {
 | 
			
		||||
 | 
			
		||||
@ -16,7 +16,7 @@
 | 
			
		||||
 | 
			
		||||
import {
 | 
			
		||||
  AdditionalDebugActionConfig,
 | 
			
		||||
  EntityDebugSettings,
 | 
			
		||||
  HasEntityDebugSettings,
 | 
			
		||||
  HasTenantId,
 | 
			
		||||
  HasVersion
 | 
			
		||||
} from '@shared/models/entity.models';
 | 
			
		||||
@ -34,8 +34,7 @@ import {
 | 
			
		||||
  endGroupHighlightRule
 | 
			
		||||
} from '@shared/models/ace/ace.models';
 | 
			
		||||
 | 
			
		||||
export interface CalculatedField extends Omit<BaseData<CalculatedFieldId>, 'label'>, HasVersion, HasTenantId, ExportableEntity<CalculatedFieldId> {
 | 
			
		||||
  debugSettings?: EntityDebugSettings;
 | 
			
		||||
export interface CalculatedField extends Omit<BaseData<CalculatedFieldId>, 'label'>, HasVersion, HasEntityDebugSettings, HasTenantId, ExportableEntity<CalculatedFieldId> {
 | 
			
		||||
  configuration: CalculatedFieldConfiguration;
 | 
			
		||||
  type: CalculatedFieldType;
 | 
			
		||||
  entityId: EntityId;
 | 
			
		||||
 | 
			
		||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user