UI: imrovements for gateway mqtt connector

This commit is contained in:
Dmitriymush 2024-03-28 18:25:00 +02:00
parent 65dc427625
commit a78e8fce5e
9 changed files with 21 additions and 359 deletions

View File

@ -18,8 +18,9 @@
<div class="tb-mapping-keys-panel">
<div class="tb-form-panel no-border no-padding">
<div class="tb-form-panel-title">{{ panelTitle | translate }}{{' (' + keysListFormArray.controls.length + ')'}}</div>
<div #scrollPanel [scrollTop]="scrollPanel.scrollHeight" class="tb-form-panel no-border no-padding key-panel" *ngIf="keysListFormArray.controls.length; else noKeys">
<div class="tb-form-panel no-border no-padding tb-flex row center align-center" *ngFor="let keyControl of keysListFormArray.controls; trackBy: trackByKey; let $index = index; let last = last;">
<div class="tb-form-panel no-border no-padding key-panel" *ngIf="keysListFormArray.controls.length; else noKeys">
<div class="tb-form-panel no-border no-padding tb-flex no-flex row center align-center fill-width"
*ngFor="let keyControl of keysListFormArray.controls; trackBy: trackByKey; let $index = index; let last = last;">
<div class="tb-form-panel stroked tb-flex">
<ng-container [formGroup]="keyControl">
<mat-expansion-panel class="tb-settings" [expanded]="last">
@ -106,7 +107,9 @@
</div>
</div>
<ng-template #noKeys>
<span class="tb-prompt tb-flex center align-center" translate>{{ noKeysText }}</span>
<div class="tb-flex no-flex center align-center key-panel">
<span class="tb-prompt" translate>{{ noKeysText }}</span>
</div>
</ng-template>
<div class="tb-flex flex-end">
<button mat-button

View File

@ -20,7 +20,7 @@
max-width: 700px;
.key-panel {
max-height: 500px;
height: 500px;
overflow: auto;
}

View File

@ -34,7 +34,6 @@ import { Store } from '@ngrx/store';
import { AppState } from '@core/core.state';
import { PageComponent } from '@shared/components/page.component';
import { isDefinedAndNotNull } from '@core/utils';
import { ValueType } from '@shared/models/constants';
import {
MappingKeysType,
MappingValueType,

View File

@ -1,175 +0,0 @@
<!--
Copyright © 2016-2024 The Thingsboard Authors
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<ng-template [formGroup]="mqttConfigFormGroup">
<mat-tab label="{{ 'gateway.broker.connection' | translate }}*">
<ng-container formGroupName="broker">
<div class="tb-form-panel no-border no-padding padding-top">
<div class="tb-form-row column-xs" fxLayoutAlign="space-between center">
<div class="fixed-title-width tb-required" translate>gateway.broker.name</div>
<div class="tb-flex no-gap">
<mat-form-field class="tb-flex no-gap" appearance="outline" subscriptSizing="dynamic">
<input matInput name="value" formControlName="name" placeholder="{{ 'gateway.set' | translate }}"/>
<mat-icon matSuffix
matTooltipPosition="above"
matTooltipClass="tb-error-tooltip"
[matTooltip]="('gateway.broker.name-required') | translate"
*ngIf="mqttConfigFormGroup.get('broker.name').hasError('required')
&& mqttConfigFormGroup.get('broker.name').touched"
class="tb-error">
warning
</mat-icon>
</mat-form-field>
</div>
</div>
<div class="tb-form-row column-xs" fxLayoutAlign="space-between center">
<div class="fixed-title-width tb-required" translate>gateway.host</div>
<div class="tb-flex no-gap">
<mat-form-field class="tb-flex no-gap" appearance="outline" subscriptSizing="dynamic">
<input matInput name="value" formControlName="host" placeholder="{{ 'gateway.set' | translate }}"/>
<mat-icon matSuffix
matTooltipPosition="above"
matTooltipClass="tb-error-tooltip"
[matTooltip]="('gateway.host-required') | translate"
*ngIf="mqttConfigFormGroup.get('broker.host').hasError('required')
&& mqttConfigFormGroup.get('broker.host').touched"
class="tb-error">
warning
</mat-icon>
</mat-form-field>
</div>
</div>
<div class="tb-form-row column-xs" fxLayoutAlign="space-between center">
<div class="fixed-title-width tb-required" translate>gateway.port</div>
<div class="tb-flex no-gap">
<mat-form-field class="tb-flex no-gap" appearance="outline" subscriptSizing="dynamic">
<input matInput name="value" formControlName="port" placeholder="{{ 'gateway.set' | translate }}"/>
<mat-icon matSuffix
matTooltipPosition="above"
matTooltipClass="tb-error-tooltip"
[matTooltip]="('gateway.port-required') | translate"
*ngIf="mqttConfigFormGroup.get('broker.port').hasError('required')
&& mqttConfigFormGroup.get('broker.port').touched"
class="tb-error">
warning
</mat-icon>
</mat-form-field>
</div>
</div>
<div class="tb-form-row column-xs" fxLayoutAlign="space-between center">
<div class="fixed-title-width" translate>gateway.mqtt-version</div>
<div class="tb-flex no-gap">
<mat-form-field class="tb-flex no-gap" appearance="outline" subscriptSizing="dynamic">
<mat-select formControlName="version">
<mat-option *ngFor="let version of mqttVersions" [value]="version.value">{{ version.name }}</mat-option>
</mat-select>
</mat-form-field>
</div>
</div>
<div class="tb-form-row column-xs" fxLayoutAlign="space-between center">
<div class="fixed-title-width" translate>gateway.client-id</div>
<div class="tb-flex no-gap">
<mat-form-field class="tb-flex no-gap" appearance="outline" subscriptSizing="dynamic">
<input matInput name="value" formControlName="clientId" placeholder="{{ 'gateway.set' | translate }}"/>
<button type="button"
matSuffix
mat-icon-button
aria-label="Generate"
matTooltip="{{ 'gateway.generate-client-id' | translate }}"
matTooltipPosition="above"
(click)="generate('broker.clientId')"
*ngIf="!mqttConfigFormGroup.get('broker.clientId').value">
<mat-icon>autorenew</mat-icon>
</button>
<!-- <ng-template #copyClientId>-->
<!-- <tb-copy-button-->
<!-- matSuffix-->
<!-- miniButton="false"-->
<!-- *ngIf="deviceCredentialsMqttFormGroup.get('clientId').value"-->
<!-- [copyText]="deviceCredentialsMqttFormGroup.get('clientId').value"-->
<!-- tooltipText="{{ 'device.copy-client-id' | translate }}"-->
<!-- tooltipPosition="above"-->
<!-- icon="content_copy">-->
<!-- </tb-copy-button>-->
<!-- </ng-template>-->
</mat-form-field>
</div>
</div>
<tb-broker-security formControlName="security">
</tb-broker-security>
</div>
</ng-container>
</mat-tab>
<mat-tab label="{{ 'gateway.data-mapping' | translate }}*">
<div class="tb-form-panel no-border no-padding padding-top tb-flex fill-height">
<tb-mapping-table formControlName="dataMapping" [mappingType]="mappingTypes.DATA"></tb-mapping-table>
</div>
</mat-tab>
<mat-tab label="{{ 'gateway.requests-mapping' | translate }}">
<div class="tb-form-panel no-border no-padding padding-top tb-flex fill-height">
<tb-mapping-table formControlName="requestsMapping" [mappingType]="mappingTypes.REQUESTS"></tb-mapping-table>
</div>
</mat-tab>
<mat-tab label="{{ 'gateway.workers-settings' | translate }}">
<div class="tb-form-panel no-border no-padding">
<ng-container formGroupName="broker">
<div class="tb-form-panel no-border no-padding padding-top">
<div class="tb-form-row column-xs" fxLayoutAlign="space-between center">
<div class="fixed-title-width tb-required" tb-hint-tooltip-icon="{{ 'gateway.max-number-of-workers-hint' | translate }}" translate>
gateway.max-number-of-workers
</div>
<div class="tb-flex no-gap" [style.padding-left.px]="66">
<mat-form-field class="tb-flex no-gap" appearance="outline" subscriptSizing="dynamic">
<input matInput name="value" type="number" min="1" formControlName="maxNumberOfWorkers" placeholder="{{ 'gateway.default' | translate }}"/>
<mat-icon matSuffix
matTooltipPosition="above"
matTooltipClass="tb-error-tooltip"
[matTooltip]="('gateway.max-number-of-workers-required') | translate"
*ngIf="mqttConfigFormGroup.get('broker.maxNumberOfWorkers').hasError('min') ||
(mqttConfigFormGroup.get('broker.maxNumberOfWorkers').hasError('required') &&
mqttConfigFormGroup.get('broker.maxNumberOfWorkers').touched)"
class="tb-error">
warning
</mat-icon>
</mat-form-field>
</div>
</div>
<div class="tb-form-row column-xs" fxLayoutAlign="space-between center">
<div class="fixed-title-width tb-required" tb-hint-tooltip-icon="{{ 'gateway.max-messages-queue-for-worker-hint' | translate }}" translate>
gateway.max-messages-queue-for-worker
</div>
<div class="tb-flex no-gap">
<mat-form-field class="tb-flex no-gap" appearance="outline" subscriptSizing="dynamic">
<input matInput name="value" type="number" min="1" formControlName="maxMessageNumberPerWorker" placeholder="{{ 'gateway.default' | translate }}"/>
<mat-icon matSuffix
matTooltipPosition="above"
matTooltipClass="tb-error-tooltip"
[matTooltip]="('gateway.max-messages-queue-for-worker-required') | translate"
*ngIf="mqttConfigFormGroup.get('broker.maxMessageNumberPerWorker').hasError('min') ||
(mqttConfigFormGroup.get('broker.maxMessageNumberPerWorker').hasError('required') &&
mqttConfigFormGroup.get('broker.maxMessageNumberPerWorker').touched)"
class="tb-error">
warning
</mat-icon>
</mat-form-field>
</div>
</div>
</div>
</ng-container>
</div>
</mat-tab>
</ng-template>

View File

@ -1,27 +0,0 @@
/**
* Copyright © 2016-2024 The Thingsboard Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
:host {
//width: 100%;
//height: 100%;
//display: block;
//
//.tb-form-row {
// .tb-form-table-header {
// min-height: 48px;
// }
//}
}

View File

@ -1,150 +0,0 @@
///
/// Copyright © 2016-2024 The Thingsboard Authors
///
/// Licensed under the Apache License, Version 2.0 (the "License");
/// you may not use this file except in compliance with the License.
/// You may obtain a copy of the License at
///
/// http://www.apache.org/licenses/LICENSE-2.0
///
/// Unless required by applicable law or agreed to in writing, software
/// distributed under the License is distributed on an "AS IS" BASIS,
/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
/// See the License for the specific language governing permissions and
/// limitations under the License.
///
import {
AfterViewInit,
ChangeDetectionStrategy,
ChangeDetectorRef,
Component,
ElementRef,
forwardRef,
Input,
NgZone,
OnDestroy,
OnInit,
ViewContainerRef
} from '@angular/core';
import { PageComponent } from '@shared/components/page.component';
import { Store } from '@ngrx/store';
import { AppState } from '@core/core.state';
import { TranslateService } from '@ngx-translate/core';
import { MatDialog } from '@angular/material/dialog';
import { DialogService } from '@core/services/dialog.service';
import { Subject } from 'rxjs';
import { takeUntil } from 'rxjs/operators';
import { Overlay } from '@angular/cdk/overlay';
import { UtilsService } from '@core/services/utils.service';
import { EntityService } from '@core/http/entity.service';
import {
ControlValueAccessor,
FormBuilder, FormGroup, NG_VALIDATORS,
NG_VALUE_ACCESSOR,
UntypedFormGroup, ValidationErrors, Validator, Validators
} from '@angular/forms';
import {
MappingTypes,
MqttVersions,
SourceTypes,
SourceTypeTranslationsMap
} from '@home/components/widget/lib/gateway/gateway-widget.models';
import { coerceBoolean } from '@shared/decorators/coercion';
import { generateSecret } from '@core/utils';
@Component({
selector: 'tb-mqtt-basic-config',
templateUrl: './mqtt-basic-config.component.html',
styleUrls: ['./mqtt-basic-config.component.scss'],
changeDetection: ChangeDetectionStrategy.OnPush,
providers: [
{
provide: NG_VALUE_ACCESSOR,
useExisting: forwardRef(() => MqttBasicConfigComponent),
multi: true
},
{
provide: NG_VALIDATORS,
useExisting: forwardRef(() => MqttBasicConfigComponent),
multi: true
}
]
})
export class MqttBasicConfigComponent extends PageComponent implements ControlValueAccessor, Validator, AfterViewInit, OnInit, OnDestroy {
mqttConfigFormGroup: UntypedFormGroup;
mqttVersions = MqttVersions;
mappingTypes = MappingTypes;
private destroy$ = new Subject<void>();
private propagateChange = (v: any) => {};
constructor(protected store: Store<AppState>,
public translate: TranslateService,
public dialog: MatDialog,
private overlay: Overlay,
private viewContainerRef: ViewContainerRef,
private dialogService: DialogService,
private entityService: EntityService,
private utils: UtilsService,
private zone: NgZone,
private cd: ChangeDetectorRef,
private elementRef: ElementRef,
private fb: FormBuilder) {
super(store);
console.log('mqttconfig inited');
}
ngOnInit() {
this.mqttConfigFormGroup = this.fb.group({
broker: this.fb.group({
name: ['', [Validators.required]],
host: ['', [Validators.required]],
port: [null, [Validators.required]],
version: [5, []],
clientId: ['', []],
maxNumberOfWorkers: [100, [Validators.required, Validators.min(1)]],
maxMessageNumberPerWorker: [10, [Validators.required, Validators.min(1)]],
security: [{}, [Validators.required]]
}),
dataMapping: [[], Validators.required],
requestsMapping: [{}, []]
});
}
ngOnDestroy() {
this.destroy$.next();
this.destroy$.complete();
}
ngAfterViewInit() {
}
registerOnChange(fn: any): void {
this.propagateChange = fn;
}
registerOnTouched(fn: any): void {}
writeValue(deviceInfo: any) {
this.mqttConfigFormGroup.patchValue(deviceInfo, {emitEvent: false});
}
validate(): ValidationErrors | null {
return this.mqttConfigFormGroup.valid ? null : {
mqttConfigForm: { valid: false }
};
}
updateView(value: any) {
this.propagateChange(value);
}
generate(formControlName: string) {
this.mqttConfigFormGroup.get(formControlName).patchValue(generateSecret(5));
}
}

View File

@ -512,7 +512,7 @@
</button>
<button mat-raised-button color="primary"
type="submit"
[disabled]="(isLoading$ | async) || mappingForm.invalid || !mappingForm.dirty">
[disabled]="(isLoading$ | async) || mappingForm.invalid || !mappingForm.dirty || !keysPopupClosed">
{{ this.data.buttonTitle | translate }}
</button>
</div>

View File

@ -82,6 +82,8 @@ export class MappingDialogComponent extends DialogComponent<MappingDialogCompone
MappingTypeTranslationsMap = MappingTypeTranslationsMap;
keysPopupClosed = true;
submitted = false;
private destroy$ = new Subject<void>();
@ -241,7 +243,9 @@ export class MappingDialogComponent extends DialogComponent<MappingDialogCompone
}
cancel(): void {
this.dialogRef.close(null);
if (this.keysPopupClosed) {
this.dialogRef.close(null);
}
}
add(): void {
@ -269,6 +273,7 @@ export class MappingDialogComponent extends DialogComponent<MappingDialogCompone
deleteKeyTitle: MappingKeysDeleteKeyTranslationsMap.get(keysType),
noKeysText: MappingKeysNoKeysTextTranslationsMap.get(keysType)
};
this.keysPopupClosed = false;
const dataKeysPanelPopover = this.popoverService.displayPopover(trigger, this.renderer,
this.viewContainerRef, MappingDataKeysPanelComponent, 'leftBottom', false, null,
ctx,
@ -278,6 +283,10 @@ export class MappingDialogComponent extends DialogComponent<MappingDialogCompone
dataKeysPanelPopover.tbComponentRef.instance.keysDataApplied.subscribe((keysData) => {
dataKeysPanelPopover.hide();
keysControl.patchValue(keysData);
keysControl.markAsDirty();
});
dataKeysPanelPopover.tbHideStart.subscribe(() => {
this.keysPopupClosed = true;
});
}
}

View File

@ -245,6 +245,9 @@
display: flex;
flex: 1;
gap: 8px;
&.no-flex {
flex: none;
}
&.row {
flex-direction: row;
}