UI: Improvement view device profile settings

This commit is contained in:
Vladyslav_Prykhodko 2020-10-08 20:43:08 +03:00
parent c7b57ca543
commit 30a6a462da
20 changed files with 154 additions and 292 deletions

View File

@ -76,7 +76,6 @@
type="button"
matTooltip="{{ (dynamicMode ? 'filter.switch-to-default-value' : 'filter.switch-to-dynamic-value') | translate }}"
matTooltipPosition="above"
*ngIf="allow"
(click)="dynamicMode = !dynamicMode">
<mat-icon class="tb-mat-20" [svgIcon]="dynamicMode ? 'mdi:numeric' : 'mdi:variable'"></mat-icon>
</button>

View File

@ -54,7 +54,7 @@ export class FilterPredicateValueComponent implements ControlValueAccessor, OnIn
if (allow) {
this.dynamicValueSourceTypes.push(DynamicValueSourceType.CURRENT_USER);
} else {
this.dynamicValueSourceTypes.push(DynamicValueSourceType.CURRENT_DEVICE);
this.dynamicValueSourceTypes = [DynamicValueSourceType.CURRENT_DEVICE];
}
}

View File

@ -90,7 +90,6 @@ import { TenantProfileDialogComponent } from './profile/tenant-profile-dialog.co
import { TenantProfileDataComponent } from './profile/tenant-profile-data.component';
import { DefaultDeviceProfileConfigurationComponent } from './profile/device/default-device-profile-configuration.component';
import { DeviceProfileConfigurationComponent } from './profile/device/device-profile-configuration.component';
import { DeviceProfileDataComponent } from './profile/device-profile-data.component';
import { DeviceProfileComponent } from './profile/device-profile.component';
import { DefaultDeviceProfileTransportConfigurationComponent } from './profile/device/default-device-profile-transport-configuration.component';
import { DeviceProfileTransportConfigurationComponent } from './profile/device/device-profile-transport-configuration.component';
@ -195,7 +194,6 @@ import { DeviceCredentialsComponent } from './device/device-credentials.componen
AlarmRuleConditionComponent,
DeviceProfileAlarmComponent,
DeviceProfileAlarmsComponent,
DeviceProfileDataComponent,
DeviceProfileComponent,
DeviceProfileDialogComponent,
AddDeviceProfileDialogComponent,
@ -277,7 +275,6 @@ import { DeviceCredentialsComponent } from './device/device-credentials.componen
AlarmRuleConditionComponent,
DeviceProfileAlarmComponent,
DeviceProfileAlarmsComponent,
DeviceProfileDataComponent,
DeviceProfileComponent,
DeviceProfileDialogComponent,
AddDeviceProfileDialogComponent,

View File

@ -45,7 +45,7 @@
labelText="device-profile.default-rule-chain"
formControlName="defaultRuleChainId">
</tb-rule-chain-autocomplete>
<mat-form-field class="mat-block">
<mat-form-field fxHide class="mat-block">
<mat-label translate>device-profile.type</mat-label>
<mat-select formControlName="type" required>
<mat-option *ngFor="let type of deviceProfileTypes" [value]="type">

View File

@ -32,6 +32,7 @@
.mat-stepper-horizontal {
.mat-horizontal-content-container {
overflow: auto;
min-height: 260px;
}
}
}

View File

@ -21,8 +21,7 @@
<tb-alarm-rule-condition fxFlex class="row"
formControlName="condition">
</tb-alarm-rule-condition>
<section class="row">
<div formGroupName="spec">
<section formGroupName="spec" class="row">
<mat-form-field class="mat-block" hideRequiredMarker>
<mat-label translate>device-profile.condition-type</mat-label>
<mat-select formControlName="type" required>
@ -89,7 +88,6 @@
</mat-error>
</mat-form-field>
</div>
</div>
</section>
</mat-tab>
<mat-tab label="{{ 'device-profile.schedule' | translate }}">

View File

@ -29,6 +29,7 @@ import { AlarmConditionType, AlarmConditionTypeTranslationMap, AlarmRule } from
import { MatDialog } from '@angular/material/dialog';
import { TimeUnit, timeUnitTranslationMap } from '@shared/models/time/time.models';
import { coerceBooleanProperty } from '@angular/cdk/coercion';
import { isDefinedAndNotNull } from '@core/utils';
@Component({
selector: 'tb-alarm-rule',
@ -117,13 +118,11 @@ export class AlarmRuleComponent implements ControlValueAccessor, OnInit, Validat
writeValue(value: AlarmRule): void {
this.modelValue = value;
if (this.modelValue?.condition?.spec === null) {
this.modelValue.condition.spec = {
type: AlarmConditionType.SIMPLE
};
if (!isDefinedAndNotNull(this.modelValue?.condition?.spec)) {
this.modelValue = Object.assign({}, this.modelValue, {condition: {spec: {type: AlarmConditionType.SIMPLE}}});
}
this.alarmRuleFormGroup.reset(this.modelValue || undefined, {emitEvent: false});
this.updateValidators(this.modelValue?.condition?.spec?.type);
this.updateValidators(this.modelValue.condition.spec.type);
}
public validate(c: FormControl) {

View File

@ -25,4 +25,8 @@
}
}
}
.tb-prompt{
margin: 30px 0;
}
}

View File

@ -1,55 +0,0 @@
<!--
Copyright © 2016-2020 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.
-->
<div [formGroup]="deviceProfileDataFormGroup" style="padding-bottom: 16px;">
<mat-accordion multi="true">
<mat-expansion-panel *ngIf="displayProfileConfiguration" [expanded]="true">
<mat-expansion-panel-header>
<mat-panel-title>
<div translate>device-profile.profile-configuration</div>
</mat-panel-title>
</mat-expansion-panel-header>
<tb-device-profile-configuration
formControlName="configuration"
required>
</tb-device-profile-configuration>
</mat-expansion-panel>
<mat-expansion-panel *ngIf="displayTransportConfiguration" [expanded]="true">
<mat-expansion-panel-header>
<mat-panel-title>
<div translate>device-profile.transport-configuration</div>
</mat-panel-title>
</mat-expansion-panel-header>
<tb-device-profile-transport-configuration
formControlName="transportConfiguration"
required>
</tb-device-profile-transport-configuration>
</mat-expansion-panel>
<mat-expansion-panel [expanded]="true">
<mat-expansion-panel-header>
<mat-panel-title>
<div>{{'device-profile.alarm-rules-with-count' | translate:
{count: deviceProfileDataFormGroup.get('alarms').value ?
deviceProfileDataFormGroup.get('alarms').value.length : 0} }}</div>
</mat-panel-title>
</mat-expansion-panel-header>
<tb-device-profile-alarms
formControlName="alarms">
</tb-device-profile-alarms>
</mat-expansion-panel>
</mat-accordion>
</div>

View File

@ -1,110 +0,0 @@
///
/// Copyright © 2016-2020 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 { Component, forwardRef, Input, OnInit } from '@angular/core';
import { ControlValueAccessor, FormBuilder, FormGroup, NG_VALUE_ACCESSOR, Validators } from '@angular/forms';
import { Store } from '@ngrx/store';
import { AppState } from '@app/core/core.state';
import { coerceBooleanProperty } from '@angular/cdk/coercion';
import {
DeviceProfileData,
DeviceProfileType,
deviceProfileTypeConfigurationInfoMap,
DeviceTransportType, deviceTransportTypeConfigurationInfoMap
} from '@shared/models/device.models';
@Component({
selector: 'tb-device-profile-data',
templateUrl: './device-profile-data.component.html',
styleUrls: [],
providers: [{
provide: NG_VALUE_ACCESSOR,
useExisting: forwardRef(() => DeviceProfileDataComponent),
multi: true
}]
})
export class DeviceProfileDataComponent implements ControlValueAccessor, OnInit {
deviceProfileDataFormGroup: FormGroup;
private requiredValue: boolean;
get required(): boolean {
return this.requiredValue;
}
@Input()
set required(value: boolean) {
this.requiredValue = coerceBooleanProperty(value);
}
@Input()
disabled: boolean;
displayProfileConfiguration: boolean;
displayTransportConfiguration: boolean;
private propagateChange = (v: any) => { };
constructor(private store: Store<AppState>,
private fb: FormBuilder) {
}
registerOnChange(fn: any): void {
this.propagateChange = fn;
}
registerOnTouched(fn: any): void {
}
ngOnInit() {
this.deviceProfileDataFormGroup = this.fb.group({
configuration: [null, Validators.required],
transportConfiguration: [null, Validators.required],
alarms: [null]
});
this.deviceProfileDataFormGroup.valueChanges.subscribe(() => {
this.updateModel();
});
}
setDisabledState(isDisabled: boolean): void {
this.disabled = isDisabled;
if (this.disabled) {
this.deviceProfileDataFormGroup.disable({emitEvent: false});
} else {
this.deviceProfileDataFormGroup.enable({emitEvent: false});
}
}
writeValue(value: DeviceProfileData | null): void {
const deviceProfileType = value?.configuration?.type;
this.displayProfileConfiguration = deviceProfileType &&
deviceProfileTypeConfigurationInfoMap.get(deviceProfileType).hasProfileConfiguration;
const deviceTransportType = value?.transportConfiguration?.type;
this.displayTransportConfiguration = deviceTransportType &&
deviceTransportTypeConfigurationInfoMap.get(deviceTransportType).hasProfileConfiguration;
this.deviceProfileDataFormGroup.patchValue({configuration: value?.configuration}, {emitEvent: false});
this.deviceProfileDataFormGroup.patchValue({transportConfiguration: value?.transportConfiguration}, {emitEvent: false});
this.deviceProfileDataFormGroup.patchValue({alarms: value?.alarms}, {emitEvent: false});
}
private updateModel() {
let deviceProfileData: DeviceProfileData = null;
if (this.deviceProfileDataFormGroup.valid) {
deviceProfileData = this.deviceProfileDataFormGroup.getRawValue();
}
this.propagateChange(deviceProfileData);
}
}

View File

@ -53,7 +53,7 @@
labelText="device-profile.default-rule-chain"
formControlName="defaultRuleChainId">
</tb-rule-chain-autocomplete>
<mat-form-field class="mat-block">
<mat-form-field fxHide class="mat-block">
<mat-label translate>device-profile.type</mat-label>
<mat-select formControlName="type" required>
<mat-option *ngFor="let type of deviceProfileTypes" [value]="type">
@ -64,21 +64,6 @@
{{ 'device-profile.type-required' | translate }}
</mat-error>
</mat-form-field>
<mat-form-field class="mat-block">
<mat-label translate>device-profile.transport-type</mat-label>
<mat-select formControlName="transportType" required>
<mat-option *ngFor="let type of deviceTransportTypes" [value]="type">
{{deviceTransportTypeTranslations.get(type) | translate}}
</mat-option>
</mat-select>
<mat-error *ngIf="entityForm.get('transportType').hasError('required')">
{{ 'device-profile.transport-type-required' | translate }}
</mat-error>
</mat-form-field>
<tb-device-profile-data
formControlName="profileData"
required>
</tb-device-profile-data>
<mat-form-field class="mat-block">
<mat-label translate>device-profile.description</mat-label>
<textarea matInput formControlName="description" rows="2"></textarea>

View File

@ -77,7 +77,11 @@ export class DeviceProfileComponent extends EntityComponent<DeviceProfile> {
name: [entity ? entity.name : '', [Validators.required]],
type: [entity ? entity.type : null, [Validators.required]],
transportType: [entity ? entity.transportType : null, [Validators.required]],
profileData: [entity && !this.isAdd ? entity.profileData : {}, []],
profileData: this.fb.group({
configuration: [entity && !this.isAdd ? entity.profileData?.configuration : {}, Validators.required],
transportConfiguration: [entity && !this.isAdd ? entity.profileData?.transportConfiguration : {}, Validators.required],
alarms: [entity && !this.isAdd ? entity.profileData?.alarms : []]
}),
defaultRuleChainId: [entity && entity.defaultRuleChainId ? entity.defaultRuleChainId.id : null, []],
description: [entity ? entity.description : '', []],
}

View File

@ -16,9 +16,5 @@
-->
<form [formGroup]="defaultDeviceProfileConfigurationFormGroup" style="padding-bottom: 16px;">
<tb-json-object-edit
[required]="required"
label="{{ 'device-profile.type-default' | translate }}"
formControlName="configuration">
</tb-json-object-edit>
</form>

View File

@ -16,9 +16,5 @@
-->
<form [formGroup]="defaultDeviceProfileTransportConfigurationFormGroup" style="padding-bottom: 16px;">
<tb-json-object-edit
[required]="required"
label="{{ 'device-profile.transport-type-default' | translate }}"
formControlName="configuration">
</tb-json-object-edit>
</form>

View File

@ -89,7 +89,7 @@ export class Lwm2mDeviceProfileTransportConfigurationComponent implements Contro
let configuration: DeviceProfileTransportConfiguration = null;
if (this.lwm2mDeviceProfileTransportConfigurationFormGroup.valid) {
configuration = this.lwm2mDeviceProfileTransportConfigurationFormGroup.getRawValue().configuration;
configuration.type = DeviceTransportType.LWM2M;
// configuration.type = DeviceTransportType.LWM2M;
}
this.propagateChange(configuration);
}

View File

@ -16,6 +16,49 @@
-->
<mat-tab *ngIf="entity"
label="{{ 'device-profile.transport-configuration' | translate }}" #transportType="matTab">
<div class="mat-padding" [formGroup]="detailsForm">
<mat-form-field class="mat-block">
<mat-label translate>device-profile.transport-type</mat-label>
<mat-select formControlName="transportType" required>
<mat-option *ngFor="let type of deviceTransportTypes" [value]="type">
{{deviceTransportTypeTranslations.get(type) | translate}}
</mat-option>
</mat-select>
<mat-error *ngIf="detailsForm.get('transportType').hasError('required')">
{{ 'device-profile.transport-type-required' | translate }}
</mat-error>
</mat-form-field>
<div formGroupName="profileData">
<tb-device-profile-transport-configuration
formControlName="transportConfiguration"
required>
</tb-device-profile-transport-configuration>
</div>
</div>
</mat-tab>
<mat-tab *ngIf="entity"
label="{{'device-profile.alarm-rules' | translate:
{count: entity.profileData.alarms.length ?
entity.profileData.alarms.length : 0} }}" #alarmRules="matTab">
<div class="mat-padding" [formGroup]="detailsForm">
<div formGroupName="profileData">
<tb-device-profile-alarms formControlName="alarms"></tb-device-profile-alarms>
</div>
</div>
</mat-tab>
<mat-tab *ngIf="false"
label="{{'device-profile.profile-configuration' | translate }}" #deviceProfile="matTab">
<div class="mat-padding" [formGroup]="detailsForm">
<div formGroupName="profileData">
<tb-device-profile-configuration
formControlName="configuration"
required>
</tb-device-profile-configuration>
</div>
</div>
</mat-tab>
<mat-tab *ngIf="entity && !isEdit"
label="{{ 'attribute.attributes' | translate }}" #attributesTab="matTab">
<tb-attribute-table [defaultAttributeScope]="attributeScopes.SERVER_SCOPE"
[active]="attributesTab.isActive"
@ -23,7 +66,7 @@
[entityName]="entity.name">
</tb-attribute-table>
</mat-tab>
<mat-tab *ngIf="entity"
<mat-tab *ngIf="entity && !isEdit"
label="{{ 'attribute.latest-telemetry' | translate }}" #telemetryTab="matTab">
<tb-attribute-table [defaultAttributeScope]="latestTelemetryTypes.LATEST_TELEMETRY"
disableAttributeScopeSelection
@ -32,11 +75,11 @@
[entityName]="entity.name">
</tb-attribute-table>
</mat-tab>
<mat-tab *ngIf="entity"
<mat-tab *ngIf="entity && !isEdit"
label="{{ 'alarm.alarms' | translate }}" #alarmsTab="matTab">
<tb-alarm-table [active]="alarmsTab.isActive" [entityId]="entity.id"></tb-alarm-table>
</mat-tab>
<mat-tab *ngIf="entity"
<mat-tab *ngIf="entity && !isEdit"
label="{{ 'tenant.events' | translate }}" #eventsTab="matTab">
<tb-event-table [defaultEventType]="eventTypes.ERROR" [active]="eventsTab.isActive" [tenantId]="nullUid"
[entityId]="entity.id"></tb-event-table>

View File

@ -18,7 +18,7 @@ import { Component } from '@angular/core';
import { Store } from '@ngrx/store';
import { AppState } from '@core/core.state';
import { EntityTabsComponent } from '../../components/entity/entity-tabs.component';
import { DeviceProfile } from '@shared/models/device.models';
import { DeviceProfile, DeviceTransportType, deviceTransportTypeTranslationMap } from '@shared/models/device.models';
@Component({
selector: 'tb-device-profile-tabs',
@ -27,6 +27,9 @@ import { DeviceProfile } from '@shared/models/device.models';
})
export class DeviceProfileTabsComponent extends EntityTabsComponent<DeviceProfile> {
deviceTransportTypes = Object.keys(DeviceTransportType);
deviceTransportTypeTranslations = deviceTransportTypeTranslationMap;
constructor(protected store: Store<AppState>) {
super(store);
}

View File

@ -59,6 +59,8 @@ export class DeviceProfilesTableConfigResolver implements Resolve<EntityTableCon
this.config.entityTranslations = entityTypeTranslations.get(EntityType.DEVICE_PROFILE);
this.config.entityResources = entityTypeResources.get(EntityType.DEVICE_PROFILE);
this.config.hideDetailsTabsOnEdit = false;
this.config.addDialogStyle = {width: '1000px'};
this.config.columns.push(

View File

@ -89,7 +89,7 @@ export class Lwm2mDeviceTransportConfigurationComponent implements ControlValueA
let configuration: DeviceTransportConfiguration = null;
if (this.lwm2mDeviceTransportConfigurationFormGroup.valid) {
configuration = this.lwm2mDeviceTransportConfigurationFormGroup.getRawValue().configuration;
configuration.type = DeviceTransportType.LWM2M;
// configuration.type = DeviceTransportType.LWM2M;
}
this.propagateChange(configuration);
}

View File

@ -33,7 +33,7 @@ export enum DeviceProfileType {
export enum DeviceTransportType {
DEFAULT = 'DEFAULT',
MQTT = 'MQTT',
LWM2M = 'LWM2M'
// LWM2M = 'LWM2M'
}
export enum MqttTransportPayloadType {
@ -68,7 +68,7 @@ export const deviceTransportTypeTranslationMap = new Map<DeviceTransportType, st
[
[DeviceTransportType.DEFAULT, 'device-profile.transport-type-default'],
[DeviceTransportType.MQTT, 'device-profile.transport-type-mqtt'],
[DeviceTransportType.LWM2M, 'device-profile.transport-type-lwm2m']
// [DeviceTransportType.LWM2M, 'device-profile.transport-type-lwm2m']
]
);
@ -104,13 +104,13 @@ export const deviceTransportTypeConfigurationInfoMap = new Map<DeviceTransportTy
hasDeviceConfiguration: true,
}
],
[
DeviceTransportType.LWM2M,
{
hasProfileConfiguration: true,
hasDeviceConfiguration: true,
}
]
// [
// DeviceTransportType.LWM2M,
// {
// hasProfileConfiguration: true,
// hasDeviceConfiguration: true,
// }
// ]
]
);
@ -188,10 +188,10 @@ export function createDeviceProfileTransportConfiguration(type: DeviceTransportT
};
transportConfiguration = {...mqttTransportConfiguration, type: DeviceTransportType.MQTT};
break;
case DeviceTransportType.LWM2M:
const lwm2mTransportConfiguration: Lwm2mDeviceProfileTransportConfiguration = {};
transportConfiguration = {...lwm2mTransportConfiguration, type: DeviceTransportType.LWM2M};
break;
// case DeviceTransportType.LWM2M:
// const lwm2mTransportConfiguration: Lwm2mDeviceProfileTransportConfiguration = {};
// transportConfiguration = {...lwm2mTransportConfiguration, type: DeviceTransportType.LWM2M};
// break;
}
}
return transportConfiguration;
@ -209,10 +209,10 @@ export function createDeviceTransportConfiguration(type: DeviceTransportType): D
const mqttTransportConfiguration: MqttDeviceTransportConfiguration = {};
transportConfiguration = {...mqttTransportConfiguration, type: DeviceTransportType.MQTT};
break;
case DeviceTransportType.LWM2M:
const lwm2mTransportConfiguration: Lwm2mDeviceTransportConfiguration = {};
transportConfiguration = {...lwm2mTransportConfiguration, type: DeviceTransportType.LWM2M};
break;
// case DeviceTransportType.LWM2M:
// const lwm2mTransportConfiguration: Lwm2mDeviceTransportConfiguration = {};
// transportConfiguration = {...lwm2mTransportConfiguration, type: DeviceTransportType.LWM2M};
// break;
}
}
return transportConfiguration;