Browse Source

Added string autocomplete for telemetry device tab (#14507)

* added string autocomplete for telemetry device tab

* fixed validation

* fixed button alignment

* fixed styles
pull/14525/head
Maksym Tsymbarov 9 months ago
committed by GitHub
parent
commit
aeb9958bf5
No known key found for this signature in database GPG Key ID: B5690EEEBB952194
  1. 22
      ui-ngx/src/app/modules/home/components/attribute/add-attribute-dialog.component.html
  2. 33
      ui-ngx/src/app/modules/home/components/attribute/add-attribute-dialog.component.ts
  3. 14
      ui-ngx/src/app/modules/home/components/attribute/attribute-table.component.ts
  4. 8
      ui-ngx/src/app/shared/components/string-autocomplete.component.html
  5. 20
      ui-ngx/src/app/shared/components/string-autocomplete.component.scss
  6. 50
      ui-ngx/src/app/shared/components/string-autocomplete.component.ts

22
ui-ngx/src/app/modules/home/components/attribute/add-attribute-dialog.component.html

@ -30,16 +30,18 @@
<div style="height: 4px;" *ngIf="!(isLoading$ | async)"></div>
<div mat-dialog-content>
<fieldset [disabled]="isLoading$ | async" class="flex flex-col gap-2">
<mat-form-field appearance="outline" subscriptSizing="dynamic" class="mat-block tb-inline-field">
<mat-label translate>attribute.key</mat-label>
<input matInput formControlName="key" required>
<mat-error *ngIf="attributeFormGroup.get('key').hasError('required')">
{{ (isTelemetry ? 'attribute.telemetry-key-required' : 'attribute.key-required') | translate }}
</mat-error>
<mat-error *ngIf="attributeFormGroup.get('key').hasError('maxlength')">
{{ 'attribute.key-max-length' | translate }}
</mat-error>
</mat-form-field>
<tb-string-autocomplete
panelWidth=""
additionalClass="mat-block tb-inline-field tb-suffix-absolute tb-suffix-show-on-hover"
required
[label]="'attribute.key' | translate"
subscriptSizing="dynamic"
appearance="outline"
[fetchOptionsFn]="isTelemetry && fetchOptions.bind(this)"
[errorMessages]="attributeErrorMessages"
[controlValidators]="keyValidators"
formControlName="key">
</tb-string-autocomplete>
<tb-value-input
formControlName="value"
[requiredText]="isTelemetry ? 'attribute.telemetry-value-required' : 'attribute.value-required'">

33
ui-ngx/src/app/modules/home/components/attribute/add-attribute-dialog.component.ts

@ -23,13 +23,23 @@ import { FormBuilder, FormControl, FormGroup, FormGroupDirective, NgForm, Valida
import { EntityId } from '@shared/models/id/entity-id';
import { Router } from '@angular/router';
import { DialogComponent } from '@app/shared/components/dialog.component';
import { AttributeData, AttributeScope, LatestTelemetry, TelemetryType } from '@shared/models/telemetry/telemetry.models';
import {
AttributeData,
AttributeScope,
LatestTelemetry,
TelemetryType
} from '@shared/models/telemetry/telemetry.models';
import { AttributeService } from '@core/http/attribute.service';
import { Observable } from 'rxjs';
import { AttributeDatasource } from '@home/models/datasource/attribute-datasource';
import { map } from 'rxjs/operators';
import { ErrorMessageConfig } from '@shared/components/string-autocomplete.component';
import { TranslateService } from '@ngx-translate/core';
export interface AddAttributeDialogData {
entityId: EntityId;
attributeScope: TelemetryType;
datasource?: AttributeDatasource;
}
@Component({
@ -47,19 +57,22 @@ export class AddAttributeDialogComponent extends DialogComponent<AddAttributeDia
isTelemetry = false;
keyValidators = [Validators.required, Validators.maxLength(255)];
constructor(protected store: Store<AppState>,
protected router: Router,
@Inject(MAT_DIALOG_DATA) public data: AddAttributeDialogData,
private attributeService: AttributeService,
@SkipSelf() private errorStateMatcher: ErrorStateMatcher,
public dialogRef: MatDialogRef<AddAttributeDialogComponent, boolean>,
public fb: FormBuilder) {
public fb: FormBuilder,
private translate: TranslateService) {
super(store, router, dialogRef);
}
ngOnInit(): void {
this.attributeFormGroup = this.fb.group({
key: ['', [Validators.required, Validators.maxLength(255)]],
key: ['', this.keyValidators],
value: [null, [Validators.required]]
});
this.isTelemetry = this.data.attributeScope === LatestTelemetry.LATEST_TELEMETRY;
@ -97,4 +110,18 @@ export class AddAttributeDialogComponent extends DialogComponent<AddAttributeDia
}
task.subscribe(() => this.dialogRef.close(true));
}
fetchOptions(searchText: string): Observable<Array<string>> {
const search = searchText ? searchText?.toLowerCase() : '';
return this.data.datasource?.getAllAttributes(this.data.entityId,this.data.attributeScope).pipe(
map(attributes => attributes?.filter(attribute => attribute.key.toLowerCase().includes(search)).map(a => a.key)),
)
}
get attributeErrorMessages(): ErrorMessageConfig {
return {
required: this.translate.instant(this.isTelemetry ? 'attribute.telemetry-key-required' : 'attribute.key-required'),
maxlength: this.translate.instant('attribute.key-max-length')
}
}
}

14
ui-ngx/src/app/modules/home/components/attribute/attribute-table.component.ts

@ -317,13 +317,19 @@ export class AttributeTableComponent extends PageComponent implements AfterViewI
if ($event) {
$event.stopPropagation();
}
const data: AddAttributeDialogData = {
entityId: this.entityIdValue,
attributeScope: this.attributeScope,
};
if(this.attributeScope === LatestTelemetry.LATEST_TELEMETRY) {
data.datasource = this.dataSource;
}
this.dialog.open<AddAttributeDialogComponent, AddAttributeDialogData, boolean>(AddAttributeDialogComponent, {
disableClose: true,
panelClass: ['tb-dialog', 'tb-fullscreen-dialog'],
data: {
entityId: this.entityIdValue,
attributeScope: this.attributeScope
}
data
}).afterClosed().subscribe(
(res) => {
if (res) {

8
ui-ngx/src/app/shared/components/string-autocomplete.component.html

@ -31,21 +31,21 @@
</button>
<mat-icon *ngIf="selectionFormControl.errors && selectionFormControl.touched && !showInlineError"
matIconSuffix
[matTooltip]="errorText"
[matTooltip]="getErrorMessage"
matTooltipPosition="above"
[matTooltipClass]="tooltipClass"
class="material-icons tb-suffix-show-always tb-error">
warning
</mat-icon>
<mat-error *ngIf="selectionFormControl.errors && showInlineError">
{{errorText}}
{{ getErrorMessage }}
</mat-error>
<mat-autocomplete
#optionsAutocomplete="matAutocomplete"
class="tb-autocomplete tb-options-input-autocomplete"
class="tb-autocomplete"
[panelWidth]="panelWidth">
<mat-option *ngFor="let option of filteredOptions$ | async" [value]="option">
<span class="tb-option-name flex-1" [innerHTML]="option | highlight:searchText:true:'ig'"></span>
<span [innerHTML]="option | highlight:searchText:true:'ig'"></span>
</mat-option>
</mat-autocomplete>
</mat-form-field>

20
ui-ngx/src/app/shared/components/string-autocomplete.component.scss

@ -25,25 +25,5 @@
margin-right: 8px;
}
}
.tb-autocomplete.tb-option-input-autocomplete {
.mat-mdc-option {
border-bottom: none;
.mdc-list-item__primary-text {
flex: 1;
display: flex;
flex-direction: row;
gap: 8px;
.tb-option {
font-size: 14px;
font-weight: 400;
line-height: 20px;
letter-spacing: 0.2px;
}
}
}
}
}
}

50
ui-ngx/src/app/shared/components/string-autocomplete.component.ts

@ -14,27 +14,25 @@
/// limitations under the License.
///
import {
Component,
Input,
forwardRef,
OnInit,
ViewChild,
ElementRef
} from '@angular/core';
import { Component, ElementRef, forwardRef, Input, OnInit, ViewChild } from '@angular/core';
import {
ControlValueAccessor,
NG_VALUE_ACCESSOR,
FormBuilder,
FormControl,
Validators,
FormBuilder
NG_VALUE_ACCESSOR,
ValidatorFn,
Validators
} from '@angular/forms';
import { Observable, of } from 'rxjs';
import { tap, map, switchMap, take } from 'rxjs/operators';
import { map, switchMap, take, tap } from 'rxjs/operators';
import { TranslateService } from '@ngx-translate/core';
import { coerceBoolean } from '@shared/decorators/coercion';
import { MatFormFieldAppearance, SubscriptSizing } from '@angular/material/form-field';
export interface ErrorMessageConfig {
[errorKey: string]: string;
}
@Component({
selector: 'tb-string-autocomplete',
templateUrl: './string-autocomplete.component.html',
@ -85,6 +83,12 @@ export class StringAutocompleteComponent implements ControlValueAccessor, OnInit
@Input()
errorText: string;
@Input()
controlValidators: ValidatorFn[] = [];
@Input()
errorMessages: ErrorMessageConfig;
@Input()
@coerceBoolean()
showInlineError = false;
@ -107,7 +111,13 @@ export class StringAutocompleteComponent implements ControlValueAccessor, OnInit
ngOnInit() {
const validators = [Validators.pattern(/.*\S.*/)];
if (this.required) {
if (this.controlValidators?.length) {
validators.push(...this.controlValidators);
const parentHasRequired = this.controlValidators.some(v => v === Validators.required);
if (this.required && !parentHasRequired) {
validators.push(Validators.required);
}
} else if (this.required) {
validators.push(Validators.required);
}
this.selectionFormControl = this.fb.control('', validators);
@ -184,4 +194,18 @@ export class StringAutocompleteComponent implements ControlValueAccessor, OnInit
this.nameInput.nativeElement.focus();
}, 0);
}
get getErrorMessage(): string {
if (!this.selectionFormControl.errors) {
return '';
}
if (this.errorMessages) {
for (const errorKey in this.selectionFormControl.errors) {
if (this.errorMessages[errorKey]) {
return this.errorMessages[errorKey];
}
}
}
return this.errorText;
}
}

Loading…
Cancel
Save