Browse Source

UI: Updated propagation cf config; updated cf style for mobile and table screen; add cf filter example

pull/14141/head
Vladyslav_Prykhodko 10 months ago
parent
commit
8fe3669eac
  1. 2
      ui-ngx/src/app/modules/home/components/calculated-fields/components/calculated-field-arguments/calculated-field-argument-panel.component.html
  2. 2
      ui-ngx/src/app/modules/home/components/calculated-fields/components/calculated-field-arguments/calculated-field-arguments-table.component.html
  3. 3
      ui-ngx/src/app/modules/home/components/calculated-fields/components/calculated-field-arguments/calculated-field-arguments-table.component.scss
  4. 3
      ui-ngx/src/app/modules/home/components/calculated-fields/components/common/calculated-field-panel.scss
  5. 2
      ui-ngx/src/app/modules/home/components/calculated-fields/components/dialog/calculated-field-dialog.component.scss
  6. 2
      ui-ngx/src/app/modules/home/components/calculated-fields/components/geofencing-configuration/calculated-field-geofencing-zone-groups-table.component.html
  7. 108
      ui-ngx/src/app/modules/home/components/calculated-fields/components/output/calculated-field-output.component.html
  8. 6
      ui-ngx/src/app/modules/home/components/calculated-fields/components/propagation-configuration/propagation-configuration.component.html
  9. 6
      ui-ngx/src/app/modules/home/components/calculated-fields/components/propagation-configuration/propagation-configuration.component.ts
  10. 29
      ui-ngx/src/app/modules/home/components/calculated-fields/components/related-entities-aggregation-configuration/calculated-field-metrics-panel.component.html
  11. 31
      ui-ngx/src/app/modules/home/components/calculated-fields/components/related-entities-aggregation-configuration/calculated-field-metrics-panel.component.ts
  12. 2
      ui-ngx/src/app/modules/home/components/calculated-fields/components/related-entities-aggregation-configuration/calculated-field-metrics-table.component.html
  13. 3
      ui-ngx/src/app/shared/models/calculated-field.models.ts
  14. 10
      ui-ngx/src/assets/help/en_US/calculated-field/filter_expression_fn.md

2
ui-ngx/src/app/modules/home/components/calculated-fields/components/calculated-field-arguments/calculated-field-argument-panel.component.html

@ -16,7 +16,7 @@
-->
<div class="tb-config-panel" [formGroup]="argumentFormGroup">
<div class="tb-config-panel-title tb-form-panel-title">{{ 'calculated-fields.metrics.metric-settings' | translate }}</div>
<div class="tb-config-panel-title tb-form-panel-title">{{ 'calculated-fields.argument-settings' | translate }}</div>
<div class="tb-config-panel-content tb-form-panel no-border no-padding">
@if (hint) {
<div class="tb-form-hint tb-primary-fill hint-container">

2
ui-ngx/src/app/modules/home/components/calculated-fields/components/calculated-field-arguments/calculated-field-arguments-table.component.html

@ -88,7 +88,7 @@
<ng-container matColumnDef="actions" stickyEnd>
<mat-header-cell *matHeaderCellDef class="w-20 min-w-20"/>
<mat-cell *matCellDef="let argument;">
<div class="tb-form-table-row-cell-buttons flex w-20 min-w-20">
<div class="tb-form-table-row-cell-buttons min-w-20">
<button type="button"
mat-icon-button
#button

3
ui-ngx/src/app/modules/home/components/calculated-fields/components/calculated-field-arguments/calculated-field-arguments-table.component.scss

@ -51,6 +51,9 @@
--mat-badge-legacy-small-size-container-size: 8px;
--mat-badge-small-size-container-overlap-offset: -5px;
--mat-badge-small-size-text-size: 0;
width: 100%;
display: flex;
justify-content: end;
}
}

3
ui-ngx/src/app/modules/home/components/calculated-fields/components/common/calculated-field-panel.scss

@ -22,6 +22,9 @@
flex-direction: column;
gap: 16px;
@media #{$mat-lt-md} {
max-width: fit-content;
}
@media #{$mat-xs} {
width: 90vw;
}

2
ui-ngx/src/app/modules/home/components/calculated-fields/components/dialog/calculated-field-dialog.component.scss

@ -17,6 +17,8 @@
.calculated-field-dialog-container {
width: 869px;
max-width: 100%;
display: grid;
grid-template-rows: min-content minmax(auto, 1fr) min-content;
--mdc-outlined-text-field-outline-color: rgba(0,0,0,0.12);
--mdc-outlined-text-field-container-shape: 6px;
--mat-form-field-trailing-icon-color: rgba(0, 0, 0, 0.56);

2
ui-ngx/src/app/modules/home/components/calculated-fields/components/geofencing-configuration/calculated-field-geofencing-zone-groups-table.component.html

@ -91,7 +91,7 @@
<ng-container matColumnDef="actions" stickyEnd>
<mat-header-cell *matHeaderCellDef class="w-20 min-w-20"/>
<mat-cell *matCellDef="let geofenceZone;">
<div class="tb-form-table-row-cell-buttons flex w-20 min-w-20">
<div class="tb-form-table-row-cell-buttons min-w-20">
<button type="button"
mat-icon-button
#button

108
ui-ngx/src/app/modules/home/components/calculated-fields/components/output/calculated-field-output.component.html

@ -17,65 +17,67 @@
-->
<div class="tb-form-panel" [formGroup]="outputForm">
<div class="tb-form-panel-title">{{ 'calculated-fields.output' | translate }}</div>
<div class="flex items-center gap-3">
<mat-form-field class="flex-1" appearance="outline" subscriptSizing="dynamic">
<mat-label>{{ 'calculated-fields.output-type' | translate }}</mat-label>
<mat-select formControlName="type">
@for (type of outputTypes; track type) {
<mat-option [value]="type">{{ OutputTypeTranslations.get(type) | translate }}</mat-option>
}
</mat-select>
</mat-form-field>
@if (outputForm.get('type').value === OutputType.Attribute
&& (entityId.entityType === EntityType.DEVICE || entityId.entityType === EntityType.DEVICE_PROFILE)) {
<div class="flex flex-col gap-3">
<div class="flex gap-3 xs:flex-col">
<mat-form-field class="flex-1" appearance="outline" subscriptSizing="dynamic">
<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 }}
</mat-option>
<mat-option [value]="AttributeScope.SHARED_SCOPE">
{{ 'calculated-fields.shared-attributes' | translate }}
</mat-option>
<mat-label>{{ 'calculated-fields.output-type' | translate }}</mat-label>
<mat-select formControlName="type">
@for (type of outputTypes; track type) {
<mat-option [value]="type">{{ OutputTypeTranslations.get(type) | translate }}</mat-option>
}
</mat-select>
</mat-form-field>
}
</div>
@if (simpleMode) {
@if (hiddenName) {
<div class="grid grid-cols-2 items-start gap-3">
<ng-container *ngTemplateOutlet="decimalsByDefaultField"></ng-container>
<ng-content select=".simpleMode"></ng-content>
</div>
} @else {
<div class="flex items-start gap-3">
@if (outputForm.get('type').value === OutputType.Attribute
&& (entityId.entityType === EntityType.DEVICE || entityId.entityType === EntityType.DEVICE_PROFILE)) {
<mat-form-field class="flex-1" appearance="outline" subscriptSizing="dynamic">
<mat-label>
{{
(outputForm.get('type').value === OutputType.Timeseries
? 'calculated-fields.timeseries-key'
: 'calculated-fields.attribute-key')
| translate
}}
</mat-label>
<input matInput formControlName="name" required>
@if (outputForm.get('name').errors && outputForm.get('name').touched) {
<mat-error>
@if (outputForm.get('name').hasError('required')) {
{{ 'common.hint.key-required' | translate }}
} @else if (outputForm.get('name').hasError('pattern')) {
{{ 'common.hint.key-pattern' | translate }}
} @else if (outputForm.get('name').hasError('maxlength')) {
{{ 'common.hint.key-max-length' | translate }}
}
</mat-error>
}
<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 }}
</mat-option>
<mat-option [value]="AttributeScope.SHARED_SCOPE">
{{ 'calculated-fields.shared-attributes' | translate }}
</mat-option>
</mat-select>
</mat-form-field>
<ng-container *ngTemplateOutlet="decimalsByDefaultField"></ng-container>
</div>
<ng-content select=".simpleMode"></ng-content>
}
</div>
@if (simpleMode) {
@if (hiddenName) {
<div class="grid grid-cols-2 items-start gap-3 xs:grid-cols-1">
<ng-container *ngTemplateOutlet="decimalsByDefaultField"></ng-container>
<ng-content select=".simpleMode"></ng-content>
</div>
} @else {
<div class="flex items-start gap-3 xs:flex-col xs:items-stretch">
<mat-form-field class="flex-1" appearance="outline" subscriptSizing="dynamic">
<mat-label>
{{
(outputForm.get('type').value === OutputType.Timeseries
? 'calculated-fields.timeseries-key'
: 'calculated-fields.attribute-key')
| translate
}}
</mat-label>
<input matInput formControlName="name" required>
@if (outputForm.get('name').errors && outputForm.get('name').touched) {
<mat-error>
@if (outputForm.get('name').hasError('required')) {
{{ 'common.hint.key-required' | translate }}
} @else if (outputForm.get('name').hasError('pattern')) {
{{ 'common.hint.key-pattern' | translate }}
} @else if (outputForm.get('name').hasError('maxlength')) {
{{ 'common.hint.key-max-length' | translate }}
}
</mat-error>
}
</mat-form-field>
<ng-container *ngTemplateOutlet="decimalsByDefaultField"></ng-container>
</div>
<ng-content select=".simpleMode"></ng-content>
}
}
}
</div>
</div>
<ng-template #decimalsByDefaultField>
<mat-form-field class="flex-1" appearance="outline" subscriptSizing="dynamic" [formGroup]="outputForm">

6
ui-ngx/src/app/modules/home/components/calculated-fields/components/propagation-configuration/propagation-configuration.component.html

@ -20,7 +20,7 @@
<div class="tb-form-panel-title" tbTruncateWithTooltip tb-hint-tooltip-icon="{{ 'calculated-fields.hint.propagation-path-related-entities' | translate }}">
{{ 'calculated-fields.propagation-path-related-entities' | translate }}
</div>
<div class="flex gap-3 xs:flex-col">
<div class="flex gap-3 xs:flex-col" formGroupName="relation">
<mat-form-field class="flex-1" appearance="outline" subscriptSizing="dynamic" hideRequiredMarker>
<mat-label>{{ 'calculated-fields.direction' | translate }}</mat-label>
<mat-select formControlName="direction">
@ -41,11 +41,11 @@
</div>
</div>
<div class="tb-form-panel">
<div class="flex flex-row items-center justify-between xs:flex-col xs:items-start xs:gap-3">
<div class="flex flex-row items-center justify-between gap-2">
<div class="tb-form-panel-title" tb-hint-tooltip-icon="{{ 'calculated-fields.hint.data-propagate' | translate }}">
{{ 'calculated-fields.data-propagate' | translate }}
</div>
<tb-toggle-select formControlName="applyExpressionToResolvedArguments">
<tb-toggle-select formControlName="applyExpressionToResolvedArguments" selectMediaBreakpoint="xs" disablePagination>
<tb-toggle-option [value]="false">{{ 'calculated-fields.propagate-type.arguments-only' | translate }}</tb-toggle-option>
<tb-toggle-option [value]="true">{{ 'calculated-fields.propagate-type.expression-result' | translate }}</tb-toggle-option>
</tb-toggle-select>

6
ui-ngx/src/app/modules/home/components/calculated-fields/components/propagation-configuration/propagation-configuration.component.ts

@ -76,8 +76,10 @@ export class PropagationConfigurationComponent implements ControlValueAccessor,
propagateConfiguration = this.fb.group({
arguments: this.fb.control({}),
applyExpressionToResolvedArguments: [false],
direction: [EntitySearchDirection.TO, Validators.required],
relationType: ['Contains', Validators.required],
relation: this.fb.group({
direction: [EntitySearchDirection.TO, Validators.required],
relationType: ['Contains', Validators.required],
}),
expression: [calculatedFieldDefaultScript],
output: this.fb.control<CalculatedFieldOutput>({
scope: AttributeScope.SERVER_SCOPE,

29
ui-ngx/src/app/modules/home/components/calculated-fields/components/related-entities-aggregation-configuration/calculated-field-metrics-panel.component.html

@ -100,7 +100,7 @@
[highlightRules]="highlightRules"
[editorCompleter]="editorCompleter"
[helpPopupStyle]="{ width: '1200px' }"
helpId="calculated-field/expression_fn">
helpId="calculated-field/filter_expression_fn">
<div toolbarPrefixButton
class="tb-primary-background tbel-script-lang-chip">{{ 'api-usage.tbel' | translate }}
</div>
@ -122,15 +122,22 @@
@if (this.metricForm.get('input.type').value === AggInputType.key) {
<div class="tb-form-row">
<div class="fixed-title-width tb-required">{{ 'calculated-fields.argument-name' | translate }}</div>
<tb-string-autocomplete [fetchOptionsFn]="fetchOptions.bind(this)"
additionalClass="tb-suffix-show-on-hover"
class="flex-1"
appearance="outline"
panelWidth=""
required
[errorText]="'calculated-fields.hint.argument-name-required' | translate"
formControlName="key">
</tb-string-autocomplete>
<mat-form-field class="flex-1" appearance="outline" subscriptSizing="dynamic">
<mat-select formControlName="key" placeholder="{{ 'action.set' | translate }}">
@for (argument of arguments; track argument) {
<mat-option [value]="argument">{{ argument }}</mat-option>
}
</mat-select>
@if (metricForm.get('input.key').touched && metricForm.get('input.key').hasError('required')) {
<mat-icon matSuffix
matTooltipPosition="above"
matTooltipClass="tb-error-tooltip"
[matTooltip]="'calculated-fields.hint.argument-name-required' | translate"
class="tb-error !block">
warning
</mat-icon>
}
</mat-form-field>
</div>
} @else {
<tb-js-func required
@ -160,7 +167,7 @@
<button mat-raised-button
color="primary"
type="button"
(click)="saveZone()"
(click)="saveMetric()"
[disabled]="metricForm.invalid || !metricForm.dirty">
{{ buttonTitle | translate }}
</button>

31
ui-ngx/src/app/modules/home/components/calculated-fields/components/related-entities-aggregation-configuration/calculated-field-metrics-panel.component.ts

@ -25,10 +25,8 @@ import {
AggInputTypeTranslations,
CalculatedFieldAggMetricValue
} from '@shared/models/calculated-field.models';
import { map } from 'rxjs/operators';
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
import { EntityFilter } from '@shared/models/query/query.models';
import { Observable, of } from 'rxjs';
import { ScriptLanguage } from '@shared/models/rule-node.models';
import { TbEditorCompleter } from '@shared/models/ace/completion.models';
import { AceHighlightRules } from '@shared/models/ace/ace.models';
@ -93,13 +91,22 @@ export class CalculatedFieldMetricsPanelComponent implements OnInit {
this.validateFilter(data.allowFilter);
this.validateInputTypeFilter(data.input?.type ?? AggInputType.key);
this.validateInputKey();
this.functionArgs = ['ctx', ...this.arguments];
}
fetchOptions(searchText: string): Observable<Array<string>> {
const search = searchText ? searchText?.toLowerCase() : '';
return of(this.arguments).pipe(map(name => name?.filter(option => option.toLowerCase().includes(search))));
saveMetric(): void {
const value = this.metricForm.value as CalculatedFieldAggMetricValuePanel;
if (!value.allowFilter) {
delete value.filter;
}
delete value.allowFilter;
this.metricDataApplied.emit(value);
}
cancel(): void {
this.popover.hide();
}
private observeFilterAllowChange(): void {
@ -134,17 +141,11 @@ export class CalculatedFieldMetricsPanelComponent implements OnInit {
}
}
saveZone(): void {
const value = this.metricForm.value as CalculatedFieldAggMetricValuePanel;
if (!value.allowFilter) {
delete value.filter;
private validateInputKey() {
if (this.metric.input?.type === AggInputType.key && !this.arguments.includes(this.metric.input.key)) {
this.metricForm.get('input.key').setValue(null);
this.metricForm.get('input.key').markAsTouched();
}
delete value.allowFilter;
this.metricDataApplied.emit(value);
}
cancel(): void {
this.popover.hide();
}
private uniqNameRequired(): ValidatorFn {

2
ui-ngx/src/app/modules/home/components/calculated-fields/components/related-entities-aggregation-configuration/calculated-field-metrics-table.component.html

@ -64,7 +64,7 @@
<ng-container matColumnDef="actions" stickyEnd>
<mat-header-cell *matHeaderCellDef class="w-20 min-w-20"/>
<mat-cell *matCellDef="let metric;">
<div class="tb-form-table-row-cell-buttons flex w-20 min-w-20">
<div class="tb-form-table-row-cell-buttons min-w-20">
<button type="button"
mat-icon-button
#button

3
ui-ngx/src/app/shared/models/calculated-field.models.ts

@ -121,8 +121,7 @@ export interface CalculatedFieldRelatedAggregationConfiguration {
interface BasePropagationConfiguration {
type: CalculatedFieldType.PROPAGATION;
direction: EntitySearchDirection;
relationType: string;
relation: RelationPathLevel;
arguments: Record<string, CalculatedFieldArgument>;
output: CalculatedFieldOutput;
}

10
ui-ngx/src/assets/help/en_US/calculated-field/filter_expression_fn.md

@ -0,0 +1,10 @@
## Calculated Field TBEL Filter Function
The **filter()** function is a user-defined script that enables custom calculations using [TBEL](${siteBaseUrl}/docs${docPlatformPrefix}/user-guide/tbel/) on telemetry and attribute data.
It receives arguments configured in the calculated field setup, along with an additional `ctx` object that stores `latestTs` and provides access to all arguments.
### Function Signature
```javascript
function calculate(ctx, arg1, arg2, ...): boolean
```
Loading…
Cancel
Save