Calculated fields adjustments, improvements and fixes

This commit is contained in:
mpetrov 2025-02-04 17:03:20 +02:00
parent 145789882e
commit eb3f6b137f
13 changed files with 91 additions and 21 deletions

View File

@ -54,7 +54,8 @@ export class CalculatedFieldsTableConfig extends EntityTableConfig<CalculatedFie
private durationLeft: DurationLeftPipe,
private popoverService: TbPopoverService,
private destroyRef: DestroyRef,
private renderer: Renderer2
private renderer: Renderer2,
public entityName: string
) {
super();
this.tableTitle = this.translate.instant('entity.type-calculated-fields');
@ -159,6 +160,7 @@ export class CalculatedFieldsTableConfig extends EntityTableConfig<CalculatedFie
entityId: this.entityId,
debugLimitsConfiguration: this.calculatedFieldsDebugPerTenantLimitsConfiguration,
tenantId: this.tenantId,
entityName: this.entityName,
}
})
.afterClosed();

View File

@ -47,6 +47,7 @@ export class CalculatedFieldsTableComponent {
active = input<boolean>();
entityId = input<EntityId>();
entityName = input<string>();
calculatedFieldsTableConfig: CalculatedFieldsTableConfig;
@ -71,7 +72,8 @@ export class CalculatedFieldsTableComponent {
this.durationLeft,
this.popoverService,
this.destroyRef,
this.renderer
this.renderer,
this.entityName()
);
this.cd.markForCheck();
}

View File

@ -80,21 +80,26 @@
</mat-chip>
</mat-chip-listbox>
</ng-container>
<div class="flex opacity-55">
<div class="flex">
<button type="button"
mat-icon-button
#button
(click)="manageArgument($event, button, $index)"
[matTooltip]="'action.edit' | translate"
matTooltipPosition="above">
<mat-icon>edit</mat-icon>
<mat-icon class="opacity-55">edit</mat-icon>
@if (argumentsFormArray.dirty
&& group.get('refEntityKey').get('type').value === ArgumentType.Rolling
&& calculatedFieldType() === CalculatedFieldType.SIMPLE) {
<tb-error class="edit-hint absolute right-0 top-0" [error]="'*'"/>
}
</button>
<button type="button"
mat-icon-button
(click)="onDelete($index)"
[matTooltip]="'action.delete' | translate"
matTooltipPosition="above">
<mat-icon>delete</mat-icon>
<mat-icon class="opacity-55">delete</mat-icon>
</button>
</div>
</div>

View File

@ -19,4 +19,9 @@
font-size: 14px;
}
}
.edit-hint {
.mat-mdc-form-field-error {
font-size: 16px;
}
}
}

View File

@ -21,7 +21,9 @@ import {
forwardRef,
input,
Input,
OnChanges,
Renderer2,
SimpleChanges,
ViewContainerRef,
} from '@angular/core';
import {
@ -70,10 +72,11 @@ import { TbPopoverComponent } from '@shared/components/popover.component';
}
],
})
export class CalculatedFieldArgumentsTableComponent implements ControlValueAccessor, Validator {
export class CalculatedFieldArgumentsTableComponent implements ControlValueAccessor, Validator, OnChanges {
@Input() entityId: EntityId;
@Input() tenantId: string;
@Input() entityName: string;
calculatedFieldType = input<CalculatedFieldType>()
@ -84,6 +87,8 @@ export class CalculatedFieldArgumentsTableComponent implements ControlValueAcces
readonly ArgumentTypeTranslations = ArgumentTypeTranslations;
readonly EntityType = EntityType;
readonly ArgumentEntityType = ArgumentEntityType;
readonly ArgumentType = ArgumentType;
readonly CalculatedFieldType = CalculatedFieldType;
private popoverComponent: TbPopoverComponent<CalculatedFieldArgumentPanelComponent>;
private propagateChange: (argumentsObj: Record<string, CalculatedFieldArgument>) => void = () => {};
@ -105,6 +110,13 @@ export class CalculatedFieldArgumentsTableComponent implements ControlValueAcces
});
}
ngOnChanges(changes: SimpleChanges): void {
if (changes.calculatedFieldType?.previousValue
&& changes.calculatedFieldType.currentValue !== changes.calculatedFieldType.previousValue) {
this.argumentsFormArray.markAsDirty();
}
}
registerOnChange(fn: (argumentsObj: Record<string, CalculatedFieldArgument>) => void): void {
this.propagateChange = fn;
}
@ -137,6 +149,7 @@ export class CalculatedFieldArgumentsTableComponent implements ControlValueAcces
calculatedFieldType: this.calculatedFieldType(),
buttonTitle: this.argumentsFormArray.at(index)?.value ? 'action.apply' : 'action.add',
tenantId: this.tenantId,
entityName: this.entityName,
};
this.popoverComponent = this.popoverService.displayPopover(trigger, this.renderer,
this.viewContainerRef, CalculatedFieldArgumentPanelComponent, 'left', false, null,
@ -202,6 +215,7 @@ export class CalculatedFieldArgumentsTableComponent implements ControlValueAcces
}),
} : {}),
refEntityKey: this.fb.group({
...value.refEntityKey,
type: [{ value: value.refEntityKey.type, disabled: true }],
key: [{ value: value.refEntityKey.key, disabled: true }],
}),

View File

@ -69,6 +69,7 @@
formControlName="arguments"
[entityId]="data.entityId"
[tenantId]="data.tenantId"
[entityName]="data.entityName"
[calculatedFieldType]="fieldFormGroup.get('type').value"
/>
</div>
@ -114,7 +115,7 @@
</mat-form-field>
@if (outputFormGroup.get('type').value === OutputType.Attribute) {
<mat-form-field class="flex-1" appearance="outline" subscriptSizing="dynamic">
<mat-label>{{ 'calculated-fields.output-type' | translate }}</mat-label>
<mat-label>{{ 'calculated-fields.attribute-scope' | translate }}</mat-label>
<mat-select formControlName="scope" class="w-full">
<mat-option [value]="AttributeScope.SERVER_SCOPE">
{{ 'calculated-fields.server-attributes' | translate }}

View File

@ -36,6 +36,7 @@ import { EntityType } from '@shared/models/entity-type.models';
import { map, startWith } from 'rxjs/operators';
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
import { ScriptLanguage } from '@shared/models/rule-node.models';
import { merge } from 'rxjs';
@Component({
selector: 'tb-calculated-field-dialog',
@ -59,10 +60,10 @@ export class CalculatedFieldDialogComponent extends DialogComponent<CalculatedFi
}),
});
functionArgs$ = this.configFormGroup.valueChanges
functionArgs$ = merge(this.configFormGroup.get('arguments').valueChanges, this.fieldFormGroup.get('type').valueChanges)
.pipe(
startWith(this.data.value?.configuration ?? {}),
map(configuration => Object.keys(configuration.arguments))
startWith(null),
map(() => Object.keys(this.configFormGroup.get('arguments').value ?? this.data.value.configuration.arguments))
);
readonly OutputTypeTranslations = OutputTypeTranslations;

View File

@ -96,7 +96,7 @@
}
</mat-form-field>
</div>
@if (entityFilter.singleEntity.id || entityType === ArgumentEntityType.Current || entityType === ArgumentEntityType.Tenant) {
@if (entityFilter.singleEntity?.id || entityType === ArgumentEntityType.Current || entityType === ArgumentEntityType.Tenant) {
@if (refEntityKeyFormGroup.get('type').value !== ArgumentType.Attribute) {
<div class="tb-form-row">
<div class="fixed-title-width tb-required">{{ 'calculated-fields.timeseries-key' | translate }}</div>

View File

@ -49,6 +49,7 @@ export class CalculatedFieldArgumentPanelComponent implements OnInit {
@Input() argument: CalculatedFieldArgumentValue;
@Input() entityId: EntityId;
@Input() tenantId: string;
@Input() entityName: string;
@Input() calculatedFieldType: CalculatedFieldType;
argumentsDataApplied = output<{ value: CalculatedFieldArgumentValue, index: number }>();
@ -83,6 +84,8 @@ export class CalculatedFieldArgumentPanelComponent implements OnInit {
readonly ArgumentEntityType = ArgumentEntityType;
readonly ArgumentEntityTypeParamsMap = ArgumentEntityTypeParamsMap;
private currentEntityFilter: EntityFilter;
constructor(
private fb: FormBuilder,
private cd: ChangeDetectorRef,
@ -107,6 +110,7 @@ export class CalculatedFieldArgumentPanelComponent implements OnInit {
ngOnInit(): void {
this.argumentFormGroup.patchValue(this.argument, {emitEvent: false});
this.currentEntityFilter = this.getCurrentEntityFilter();
this.updateEntityFilter(this.argument.refEntityId?.entityType, true);
this.toggleByEntityKeyType(this.argument.refEntityKey?.type);
this.setInitialEntityKeyType();
@ -138,30 +142,53 @@ export class CalculatedFieldArgumentPanelComponent implements OnInit {
}
private updateEntityFilter(entityType: ArgumentEntityType = ArgumentEntityType.Current, onInit = false): void {
let entityId: EntityId;
let entityFilter: EntityFilter;
switch (entityType) {
case ArgumentEntityType.Current:
entityId = this.entityId
entityFilter = this.currentEntityFilter;
break;
case ArgumentEntityType.Tenant:
entityId = {
entityFilter = {
type: AliasFilterType.singleEntity,
singleEntity: {
id: this.tenantId,
entityType: EntityType.TENANT
},
};
break;
default:
entityId = this.argumentFormGroup.get('refEntityId').value as unknown as EntityId;
entityFilter = {
type: AliasFilterType.singleEntity,
singleEntity: this.argumentFormGroup.get('refEntityId').value as unknown as EntityId,
};
}
if (!onInit) {
this.argumentFormGroup.get('refEntityKey').get('key').setValue('');
}
this.entityFilter = {
type: AliasFilterType.singleEntity,
singleEntity: entityId,
};
this.entityFilter = entityFilter;
this.cd.markForCheck();
}
private getCurrentEntityFilter(): EntityFilter {
switch (this.entityId.entityType) {
case EntityType.ASSET_PROFILE:
return {
deviceTypes: [this.entityName],
type: AliasFilterType.assetType
};
case EntityType.DEVICE_PROFILE:
return {
deviceTypes: [this.entityName],
type: AliasFilterType.deviceType
};
default:
return {
type: AliasFilterType.singleEntity,
singleEntity: this.entityId,
};
}
}
private observeEntityFilterChanges(): void {
merge(
this.refEntityIdFormGroup.get('entityType').valueChanges,

View File

@ -15,6 +15,10 @@
limitations under the License.
-->
<mat-tab *ngIf="entity && authUser.authority === authorities.TENANT_ADMIN && !isEdit"
label="{{ 'entity.type-calculated-fields' | translate }}" #calculatedFieldsTab="matTab">
<tb-calculated-fields-table [active]="calculatedFieldsTab.isActive" [entityId]="entity.id" [entityName]="entity.name"/>
</mat-tab>
<mat-tab *ngIf="entity && !isEdit" #auditLogsTab="matTab"
label="{{ 'audit-log.audit-logs' | translate }}">
<tb-audit-log-table detailsMode="true" [active]="auditLogsTab.isActive" [auditLogMode]="auditLogModes.ENTITY" [entityId]="entity.id"></tb-audit-log-table>

View File

@ -32,6 +32,10 @@
[entityName]="entity.name">
</tb-attribute-table>
</mat-tab>
<mat-tab *ngIf="entity && authUser.authority === authorities.TENANT_ADMIN"
label="{{ 'entity.type-calculated-fields' | translate }}" #calculatedFieldsTab="matTab">
<tb-calculated-fields-table [active]="calculatedFieldsTab.isActive" [entityId]="entity.id"/>
</mat-tab>
<mat-tab *ngIf="entity"
label="{{ 'alarm.alarms' | translate }}" #alarmsTab="matTab">
<tb-alarm-table [active]="alarmsTab.isActive" [entityId]="entity.id"></tb-alarm-table>

View File

@ -69,6 +69,10 @@
</div>
</div>
</mat-tab>
<mat-tab *ngIf="entity && authUser.authority === authorities.TENANT_ADMIN && !isEdit"
label="{{ 'entity.type-calculated-fields' | translate }}" #calculatedFieldsTab="matTab">
<tb-calculated-fields-table [active]="calculatedFieldsTab.isActive" [entityId]="entity.id" [entityName]="entity.name"/>
</mat-tab>
<mat-tab *ngIf="entity && !isEdit" #auditLogsTab="matTab"
label="{{ 'audit-log.audit-logs' | translate }}">
<tb-audit-log-table detailsMode="true" [active]="auditLogsTab.isActive" [auditLogMode]="auditLogModes.ENTITY" [entityId]="entity.id"></tb-audit-log-table>

View File

@ -120,6 +120,7 @@ export interface CalculatedFieldDialogData {
entityId: EntityId;
debugLimitsConfiguration: string;
tenantId: string;
entityName?: string;
}
export interface ArgumentEntityTypeParams {