Merge pull request #12743 from maxunbearable/feature/calculated-fields-adjustments-24-02

Calculated fields adjustments
This commit is contained in:
Vladyslav Prykhodko 2025-02-26 15:41:51 +02:00 committed by GitHub
commit bd1e67a185
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
5 changed files with 227 additions and 171 deletions

View File

@ -16,108 +16,112 @@
--> -->
<div class="flex flex-col gap-3"> <div class="flex flex-col gap-3">
<div class="tb-form-table"> <div class="tb-form-panel stroked no-padding no-gap arguments-table flex flex-col" [class.arguments-table-with-error]="errorText">
<div class="tb-form-table-header"> <table mat-table [dataSource]="dataSource" class="overflow-hidden bg-transparent" matSort
<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> [matSortActive]="sortOrder.property" [matSortDirection]="sortOrder.direction" matSortDisableClear>
<div class="tb-form-table-header-cell w-1/3 xs:hidden">{{ 'calculated-fields.datasource' | translate }}</div> <ng-container [matColumnDef]="'name'">
<div class="tb-form-table-header-cell w-1/6 lt-md:hidden">{{ 'common.type' | translate }}</div> <mat-header-cell mat-sort-header *matHeaderCellDef class="!w-1/4 xs:!w-1/2">
<div class="tb-form-table-header-cell w-1/6 xs:w-1/3">{{ 'entity.key' | translate }}</div> <div tbTruncateWithTooltip>{{ 'common.name' | translate }}</div>
<div class="tb-form-table-header-cell w-20 min-w-20"></div> </mat-header-cell>
</div> <mat-cell *matCellDef="let argument" class="w-1/4 xs:w-1/2">
<div class="tb-form-table-body"> <div tbTruncateWithTooltip>{{ argument.argumentName }}</div>
@for (group of argumentsFormArray.controls; track group) { </mat-cell>
<div [formGroup]="group" class="tb-form-table-row"> </ng-container>
<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"> <ng-container [matColumnDef]="'entityType'">
<input matInput formControlName="argumentName" placeholder="{{ 'action.set' | translate }}"> <mat-header-cell mat-sort-header *matHeaderCellDef class="entity-type-header w-1/4 xs:hidden">
</mat-form-field> {{ 'entity.entity-type' | translate }}
<section class="datasource-field flex w-1/3 gap-2 xs:hidden"> </mat-header-cell>
@if (group.get('refEntityId')?.get('id')?.value) { <mat-cell *matCellDef="let argument" class="w-1/4 xs:hidden">
<ng-container [formGroup]="group.get('refEntityId')"> <div tbTruncateWithTooltip>
<mat-form-field appearance="outline" class="tb-inline-field w-1/2" subscriptSizing="dynamic"> @if (argument.refEntityId?.entityType === ArgumentEntityType.Tenant) {
<mat-select [value]="group.get('refEntityId').get('entityType').value" formControlName="entityType"> {{ 'calculated-fields.argument-current-tenant' | translate }}
<mat-option [value]="group.get('refEntityId').get('entityType').value"> } @else if (argument.refEntityId?.id) {
{{ entityTypeTranslations.get(group.get('refEntityId').get('entityType').value)?.type | translate }} {{ entityTypeTranslations.get(argument.refEntityId.entityType).type | translate }}
</mat-option> } @else {
</mat-select> {{ 'calculated-fields.argument-current' | translate }}
</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"
/>
</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>
<div tbTruncateWithTooltip>
{{ group.get('refEntityKey').get('key').value }}
</div>
</mat-chip>
</mat-chip-listbox>
</ng-container>
<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)"
[matTooltip]="'action.edit' | translate"
matTooltipPosition="above">
<mat-icon
[matBadgeHidden]="!(group.get('refEntityKey').get('type').value === ArgumentType.Rolling
&& calculatedFieldType === CalculatedFieldType.SIMPLE)"
matBadgeColor="warn"
matBadgeSize="small"
matBadge="*"
>
edit
</mat-icon>
</button>
<button type="button"
mat-icon-button
(click)="onDelete($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>
} }
</div> </div>
@if (errorText) { </mat-cell>
<tb-error noMargin [error]="errorText | translate" class="pl-3"/> </ng-container>
} <ng-container [matColumnDef]="'target'">
</div> <mat-header-cell *matHeaderCellDef class="w-1/4 xs:hidden">
<div> {{ 'entity-view.target-entity' | translate }}
<button type="button" mat-stroked-button color="primary" #button (click)="manageArgument($event, button)"> </mat-header-cell>
{{ 'calculated-fields.add-argument' | translate }} <mat-cell *matCellDef="let argument" class="w-1/4 xs:hidden">
</button> <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, argument, $index)"
[matTooltip]="'action.edit' | translate"
matTooltipPosition="above">
<mat-icon
[matBadgeHidden]="!(argument.refEntityKey.type === ArgumentType.Rolling
&& calculatedFieldType === CalculatedFieldType.SIMPLE)"
matBadgeColor="warn"
matBadgeSize="small"
matBadge="*"
>
edit
</mat-icon>
</button>
<button type="button"
mat-icon-button
(click)="onDelete($event, $index)"
[matTooltip]="'action.delete' | translate"
matTooltipPosition="above">
<mat-icon>delete</mat-icon>
</button>
</div>
</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> </div>
@if (errorText) {
<tb-error noMargin [error]="errorText | translate" class="flex h-9 items-center pl-3"/>
}
</div>
<div>
<button type="button" mat-stroked-button color="primary" #button (click)="manageArgument($event, button)">
{{ 'calculated-fields.add-argument' | translate }}
</button>
</div>
</div> </div>

View File

@ -13,11 +13,21 @@
* See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
* limitations under the License. * limitations under the License.
*/ */
@use '../../../../../../../scss/constants' as constants;
:host { :host {
.entity-key-field { .arguments-table {
border-bottom: 1px solid rgba(0, 0, 0, 0.12); 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 { .tb-form-table-row-cell-buttons {
@ -25,38 +35,24 @@
--mat-badge-small-size-container-overlap-offset: -5px; --mat-badge-small-size-container-overlap-offset: -5px;
--mat-badge-small-size-text-size: 0; --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 { :host ::ng-deep {
.entity-field {
a {
font-size: 14px;
white-space: nowrap;
display: block;
overflow: hidden;
text-overflow: ellipsis;
}
}
.mat-mdc-standard-chip { .mat-mdc-standard-chip {
.mdc-evolution-chip__cell--primary, .mdc-evolution-chip__action--primary, .mdc-evolution-chip__text-label { .mdc-evolution-chip__cell--primary, .mdc-evolution-chip__action--primary, .mdc-evolution-chip__text-label {
overflow: hidden; 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;
}
}
} }

View File

@ -15,20 +15,22 @@
/// ///
import { import {
AfterViewInit,
ChangeDetectorRef, ChangeDetectorRef,
Component, Component,
DestroyRef,
forwardRef, forwardRef,
Input, Input,
OnChanges, OnChanges,
Renderer2, Renderer2,
SimpleChanges, SimpleChanges,
ViewChild,
ViewContainerRef, ViewContainerRef,
} from '@angular/core'; } from '@angular/core';
import { import {
AbstractControl, AbstractControl,
ControlValueAccessor, ControlValueAccessor,
FormBuilder, FormBuilder,
FormGroup,
NG_VALIDATORS, NG_VALIDATORS,
NG_VALUE_ACCESSOR, NG_VALUE_ACCESSOR,
ValidationErrors, ValidationErrors,
@ -48,8 +50,11 @@ 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 { isDefined, isDefinedAndNotNull } from '@core/utils'; import { getEntityDetailsPageURL, isDefined, isDefinedAndNotNull } 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 { EntityService } from '@core/http/entity.service';
import { MatSort } from '@angular/material/sort';
@Component({ @Component({
selector: 'tb-calculated-field-arguments-table', 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() entityId: EntityId;
@Input() tenantId: string; @Input() tenantId: string;
@Input() entityName: string; @Input() entityName: string;
@Input() calculatedFieldType: CalculatedFieldType; @Input() calculatedFieldType: CalculatedFieldType;
@ViewChild(MatSort, { static: true }) sort: MatSort;
errorText = ''; errorText = '';
argumentsFormArray = this.fb.array<AbstractControl>([]); argumentsFormArray = this.fb.array<AbstractControl>([]);
entityNameMap = new Map<string, string>();
sortOrder = { direction: 'asc', property: '' };
dataSource = new CalculatedFieldArgumentDatasource();
readonly entityTypeTranslations = entityTypeTranslations; readonly entityTypeTranslations = entityTypeTranslations;
readonly ArgumentTypeTranslations = ArgumentTypeTranslations; readonly ArgumentTypeTranslations = ArgumentTypeTranslations;
readonly EntityType = EntityType;
readonly ArgumentEntityType = ArgumentEntityType; readonly ArgumentEntityType = ArgumentEntityType;
readonly ArgumentType = ArgumentType; readonly ArgumentType = ArgumentType;
readonly CalculatedFieldType = CalculatedFieldType; readonly CalculatedFieldType = CalculatedFieldType;
@ -93,10 +102,14 @@ export class CalculatedFieldArgumentsTableComponent implements ControlValueAcces
private popoverService: TbPopoverService, private popoverService: TbPopoverService,
private viewContainerRef: ViewContainerRef, private viewContainerRef: ViewContainerRef,
private cd: ChangeDetectorRef, private cd: ChangeDetectorRef,
private renderer: Renderer2 private renderer: Renderer2,
private entityService: EntityService,
private destroyRef: DestroyRef,
) { ) {
this.argumentsFormArray.valueChanges.pipe(takeUntilDestroyed()).subscribe(() => { this.argumentsFormArray.valueChanges.pipe(takeUntilDestroyed()).subscribe(value => {
this.propagateChange(this.getArgumentsObject()); 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 { registerOnChange(fn: (argumentsObj: Record<string, CalculatedFieldArgument>) => void): void {
this.propagateChange = fn; this.propagateChange = fn;
} }
@ -118,12 +139,13 @@ export class CalculatedFieldArgumentsTableComponent implements ControlValueAcces
return this.errorText ? { argumentsFormArray: false } : null; return this.errorText ? { argumentsFormArray: false } : null;
} }
onDelete(index: number): void { onDelete($event: Event, index: number): void {
$event.stopPropagation();
this.argumentsFormArray.removeAt(index); this.argumentsFormArray.removeAt(index);
this.argumentsFormArray.markAsDirty(); this.argumentsFormArray.markAsDirty();
} }
manageArgument($event: Event, matButton: MatButton, index?: number): void { manageArgument($event: Event, matButton: MatButton, argument = {} as CalculatedFieldArgumentValue, index?: number): void {
$event?.stopPropagation(); $event?.stopPropagation();
if (this.popoverComponent && !this.popoverComponent.tbHidden) { if (this.popoverComponent && !this.popoverComponent.tbHidden) {
this.popoverComponent.hide(); this.popoverComponent.hide();
@ -132,16 +154,15 @@ export class CalculatedFieldArgumentsTableComponent implements ControlValueAcces
if (this.popoverService.hasPopover(trigger)) { if (this.popoverService.hasPopover(trigger)) {
this.popoverService.hidePopover(trigger); this.popoverService.hidePopover(trigger);
} else { } else {
const argumentObj = this.argumentsFormArray.at(index)?.getRawValue() ?? {};
const ctx = { const ctx = {
index, index,
argument: argumentObj, argument,
entityId: this.entityId, entityId: this.entityId,
calculatedFieldType: this.calculatedFieldType, calculatedFieldType: this.calculatedFieldType,
buttonTitle: this.argumentsFormArray.at(index)?.value ? 'action.apply' : 'action.add', buttonTitle: this.argumentsFormArray.at(index)?.value ? 'action.apply' : 'action.add',
tenantId: this.tenantId, tenantId: this.tenantId,
entityName: this.entityName, 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.popoverComponent = this.popoverService.displayPopover(trigger, this.renderer,
this.viewContainerRef, CalculatedFieldArgumentPanelComponent, isDefined(index) ? 'left' : 'right', false, null, this.viewContainerRef, CalculatedFieldArgumentPanelComponent, isDefined(index) ? 'left' : 'right', false, null,
@ -150,7 +171,7 @@ export class CalculatedFieldArgumentsTableComponent implements ControlValueAcces
{}, {}, true); {}, {}, true);
this.popoverComponent.tbComponentRef.instance.argumentsDataApplied.subscribe(({ value, index }) => { this.popoverComponent.tbComponentRef.instance.argumentsDataApplied.subscribe(({ value, index }) => {
this.popoverComponent.hide(); this.popoverComponent.hide();
const formGroup = this.getArgumentFormGroup(value); const formGroup = this.fb.group(value);
if (isDefinedAndNotNull(index)) { if (isDefinedAndNotNull(index)) {
this.argumentsFormArray.setControl(index, formGroup); this.argumentsFormArray.setControl(index, formGroup);
} else { } 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 { private updateErrorText(): void {
if (this.calculatedFieldType === CalculatedFieldType.SIMPLE 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'; this.errorText = 'calculated-fields.hint.arguments-simple-with-rolling';
} else if (!this.argumentsFormArray.controls.length) { } else if (!this.argumentsFormArray.controls.length) {
this.errorText = 'calculated-fields.hint.arguments-empty'; this.errorText = 'calculated-fields.hint.arguments-empty';
@ -173,9 +199,9 @@ export class CalculatedFieldArgumentsTableComponent implements ControlValueAcces
} }
} }
private getArgumentsObject(): Record<string, CalculatedFieldArgument> { private getArgumentsObject(value: CalculatedFieldArgumentValue[]): Record<string, CalculatedFieldArgument> {
return this.argumentsFormArray.getRawValue().reduce((acc, rawValue) => { return value.reduce((acc, argumentValue) => {
const { argumentName, ...argument } = rawValue as CalculatedFieldArgumentValue; const { argumentName, ...argument } = argumentValue as CalculatedFieldArgumentValue;
acc[argumentName] = argument; acc[argumentName] = argument;
return acc; return acc;
}, {} as Record<string, CalculatedFieldArgument>); }, {} as Record<string, CalculatedFieldArgument>);
@ -186,31 +212,62 @@ export class CalculatedFieldArgumentsTableComponent implements ControlValueAcces
this.populateArgumentsFormArray(argumentsObj) this.populateArgumentsFormArray(argumentsObj)
} }
getEntityDetailsPageURL(id: string, type: EntityType): string {
return getEntityDetailsPageURL(id, type);
}
private populateArgumentsFormArray(argumentsObj: Record<string, CalculatedFieldArgument>): void { private populateArgumentsFormArray(argumentsObj: Record<string, CalculatedFieldArgument>): void {
Object.keys(argumentsObj).forEach(key => { Object.keys(argumentsObj).forEach(key => {
const value: CalculatedFieldArgumentValue = { const value: CalculatedFieldArgumentValue = {
...argumentsObj[key], ...argumentsObj[key],
argumentName: 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 { private getSortValue(argument: CalculatedFieldArgumentValue, column: string): string {
return this.fb.group({ switch (column) {
...value, case 'entityType':
argumentName: [{ value: value.argumentName, disabled: true }], if (argument.refEntityId?.entityType === ArgumentEntityType.Tenant) {
...(value.refEntityId ? { return 'calculated-fields.argument-current-tenant';
refEntityId: this.fb.group({ } else if (argument.refEntityId?.id) {
entityType: [{ value: value.refEntityId.entityType, disabled: true }], return entityTypeTranslations.get((argument.refEntityId)?.entityType as unknown as EntityType).type;
id: [{ value: value.refEntityId.id , disabled: true }], } else {
}), return 'calculated-fields.argument-current';
} : {}), }
refEntityKey: this.fb.group({ case 'type':
...value.refEntityKey, return ArgumentTypeTranslations.get(argument.refEntityKey.type);
type: [{ value: value.refEntityKey.type, disabled: true }], case 'key':
key: [{ value: value.refEntityKey.key, disabled: true }], 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();
} }
} }

View File

@ -143,10 +143,10 @@ export class CalculatedFieldDialogComponent extends DialogComponent<CalculatedFi
} }
private applyDialogData(): void { 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 { expression, ...restConfig } = configuration as CalculatedFieldConfiguration;
const updatedConfig = { ...restConfig , ['expression'+type]: expression }; 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 { private observeTypeChanges(): void {

View File

@ -16,7 +16,7 @@
import { import {
AdditionalDebugActionConfig, AdditionalDebugActionConfig,
EntityDebugSettings, HasEntityDebugSettings,
HasTenantId, HasTenantId,
HasVersion HasVersion
} from '@shared/models/entity.models'; } from '@shared/models/entity.models';
@ -34,8 +34,7 @@ import {
endGroupHighlightRule endGroupHighlightRule
} from '@shared/models/ace/ace.models'; } from '@shared/models/ace/ace.models';
export interface CalculatedField extends Omit<BaseData<CalculatedFieldId>, 'label'>, HasVersion, HasTenantId, ExportableEntity<CalculatedFieldId> { export interface CalculatedField extends Omit<BaseData<CalculatedFieldId>, 'label'>, HasVersion, HasEntityDebugSettings, HasTenantId, ExportableEntity<CalculatedFieldId> {
debugSettings?: EntityDebugSettings;
configuration: CalculatedFieldConfiguration; configuration: CalculatedFieldConfiguration;
type: CalculatedFieldType; type: CalculatedFieldType;
entityId: EntityId; entityId: EntityId;