Add Device Wizard improvements
This commit is contained in:
parent
e10696087e
commit
c7b57ca543
@ -85,7 +85,7 @@
|
|||||||
</mat-step>
|
</mat-step>
|
||||||
<mat-step [stepControl]="alarmRulesFormGroup">
|
<mat-step [stepControl]="alarmRulesFormGroup">
|
||||||
<form [formGroup]="alarmRulesFormGroup" style="padding-bottom: 16px;">
|
<form [formGroup]="alarmRulesFormGroup" style="padding-bottom: 16px;">
|
||||||
<ng-template matStepLabel>{{'device-profile.alarm-rules' | translate:
|
<ng-template matStepLabel>{{'device-profile.alarm-rules-with-count' | translate:
|
||||||
{count: alarmRulesFormGroup.get('alarms').value ?
|
{count: alarmRulesFormGroup.get('alarms').value ?
|
||||||
alarmRulesFormGroup.get('alarms').value.length : 0} }}</ng-template>
|
alarmRulesFormGroup.get('alarms').value.length : 0} }}</ng-template>
|
||||||
<tb-device-profile-alarms
|
<tb-device-profile-alarms
|
||||||
|
|||||||
@ -20,9 +20,9 @@ import {
|
|||||||
EventEmitter,
|
EventEmitter,
|
||||||
forwardRef,
|
forwardRef,
|
||||||
Input,
|
Input,
|
||||||
NgZone,
|
NgZone, OnChanges,
|
||||||
OnInit,
|
OnInit,
|
||||||
Output,
|
Output, SimpleChanges,
|
||||||
ViewChild
|
ViewChild
|
||||||
} from '@angular/core';
|
} from '@angular/core';
|
||||||
import { ControlValueAccessor, FormBuilder, FormGroup, NG_VALUE_ACCESSOR } from '@angular/forms';
|
import { ControlValueAccessor, FormBuilder, FormGroup, NG_VALUE_ACCESSOR } from '@angular/forms';
|
||||||
@ -55,7 +55,7 @@ import { AddDeviceProfileDialogComponent, AddDeviceProfileDialogData } from './a
|
|||||||
multi: true
|
multi: true
|
||||||
}]
|
}]
|
||||||
})
|
})
|
||||||
export class DeviceProfileAutocompleteComponent implements ControlValueAccessor, OnInit {
|
export class DeviceProfileAutocompleteComponent implements ControlValueAccessor, OnInit, OnChanges {
|
||||||
|
|
||||||
selectDeviceProfileFormGroup: FormGroup;
|
selectDeviceProfileFormGroup: FormGroup;
|
||||||
|
|
||||||
@ -168,11 +168,22 @@ export class DeviceProfileAutocompleteComponent implements ControlValueAccessor,
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ngOnChanges(changes: SimpleChanges): void {
|
||||||
|
for (const propName of Object.keys(changes)) {
|
||||||
|
const change = changes[propName];
|
||||||
|
if (!change.firstChange && change.currentValue !== change.previousValue) {
|
||||||
|
if (propName === 'transportType') {
|
||||||
|
this.writeValue(null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
selectDefaultDeviceProfileIfNeeded(): void {
|
selectDefaultDeviceProfileIfNeeded(): void {
|
||||||
if (this.selectDefaultProfile && !this.modelValue) {
|
if (this.selectDefaultProfile && !this.modelValue) {
|
||||||
this.deviceProfileService.getDefaultDeviceProfileInfo().subscribe(
|
this.deviceProfileService.getDefaultDeviceProfileInfo().subscribe(
|
||||||
(profile) => {
|
(profile) => {
|
||||||
if (profile) {
|
if (profile && !this.transportType || (profile.transportType === this.transportType)) {
|
||||||
this.selectDeviceProfileFormGroup.get('deviceProfile').patchValue(profile, {emitEvent: false});
|
this.selectDeviceProfileFormGroup.get('deviceProfile').patchValue(profile, {emitEvent: false});
|
||||||
this.updateView(profile);
|
this.updateView(profile);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -42,7 +42,7 @@
|
|||||||
<mat-expansion-panel [expanded]="true">
|
<mat-expansion-panel [expanded]="true">
|
||||||
<mat-expansion-panel-header>
|
<mat-expansion-panel-header>
|
||||||
<mat-panel-title>
|
<mat-panel-title>
|
||||||
<div>{{'device-profile.alarm-rules' | translate:
|
<div>{{'device-profile.alarm-rules-with-count' | translate:
|
||||||
{count: deviceProfileDataFormGroup.get('alarms').value ?
|
{count: deviceProfileDataFormGroup.get('alarms').value ?
|
||||||
deviceProfileDataFormGroup.get('alarms').value.length : 0} }}</div>
|
deviceProfileDataFormGroup.get('alarms').value.length : 0} }}</div>
|
||||||
</mat-panel-title>
|
</mat-panel-title>
|
||||||
|
|||||||
@ -15,7 +15,7 @@
|
|||||||
limitations under the License.
|
limitations under the License.
|
||||||
|
|
||||||
-->
|
-->
|
||||||
<div style="min-width: 1000px;">
|
<div>
|
||||||
<mat-toolbar color="primary">
|
<mat-toolbar color="primary">
|
||||||
<h2 translate>device.add-device-text</h2>
|
<h2 translate>device.add-device-text</h2>
|
||||||
<span fxFlex></span>
|
<span fxFlex></span>
|
||||||
@ -29,7 +29,7 @@
|
|||||||
</mat-progress-bar>
|
</mat-progress-bar>
|
||||||
<div style="height: 4px;" *ngIf="!(isLoading$ | async)"></div>
|
<div style="height: 4px;" *ngIf="!(isLoading$ | async)"></div>
|
||||||
<div mat-dialog-content>
|
<div mat-dialog-content>
|
||||||
<mat-horizontal-stepper [linear]="true" #addDeviceWizardStepper (selectionChange)="changeStep($event)">
|
<mat-horizontal-stepper [linear]="true" [labelPosition]="labelPosition" #addDeviceWizardStepper (selectionChange)="changeStep($event)">
|
||||||
<ng-template matStepperIcon="edit">
|
<ng-template matStepperIcon="edit">
|
||||||
<mat-icon>check</mat-icon>
|
<mat-icon>check</mat-icon>
|
||||||
</ng-template>
|
</ng-template>
|
||||||
@ -48,17 +48,49 @@
|
|||||||
<mat-label translate>device.label</mat-label>
|
<mat-label translate>device.label</mat-label>
|
||||||
<input matInput formControlName="label">
|
<input matInput formControlName="label">
|
||||||
</mat-form-field>
|
</mat-form-field>
|
||||||
<mat-form-field class="mat-block">
|
<mat-form-field class="mat-block" style="padding-bottom: 14px;">
|
||||||
<mat-label translate>device-profile.transport-type</mat-label>
|
<mat-label translate>device-profile.transport-type</mat-label>
|
||||||
<mat-select formControlName="transportType" required>
|
<mat-select formControlName="transportType" required>
|
||||||
<mat-option *ngFor="let type of deviceTransportTypes" [value]="type">
|
<mat-option *ngFor="let type of deviceTransportTypes" [value]="type">
|
||||||
{{deviceTransportTypeTranslations.get(type) | translate}}
|
{{deviceTransportTypeTranslations.get(type) | translate}}
|
||||||
</mat-option>
|
</mat-option>
|
||||||
</mat-select>
|
</mat-select>
|
||||||
|
<mat-hint *ngIf="deviceWizardFormGroup.get('transportType').value">
|
||||||
|
{{deviceTransportTypeHints.get(deviceWizardFormGroup.get('transportType').value) | translate}}
|
||||||
|
</mat-hint>
|
||||||
<mat-error *ngIf="deviceWizardFormGroup.get('transportType').hasError('required')">
|
<mat-error *ngIf="deviceWizardFormGroup.get('transportType').hasError('required')">
|
||||||
{{ 'device-profile.transport-type-required' | translate }}
|
{{ 'device-profile.transport-type-required' | translate }}
|
||||||
</mat-error>
|
</mat-error>
|
||||||
</mat-form-field>
|
</mat-form-field>
|
||||||
|
<div fxLayout="row" fxLayoutGap="16px">
|
||||||
|
<mat-radio-group fxLayout="column" formControlName="addProfileType" fxLayoutAlign="space-around">
|
||||||
|
<mat-radio-button [value]="0" color="primary">
|
||||||
|
<span translate>device.wizard.existing-device-profile</span>
|
||||||
|
</mat-radio-button>
|
||||||
|
<mat-radio-button [value]="1" color="primary">
|
||||||
|
<span translate>device.wizard.new-device-profile</span>
|
||||||
|
</mat-radio-button>
|
||||||
|
</mat-radio-group>
|
||||||
|
<div fxLayout="column">
|
||||||
|
<tb-device-profile-autocomplete
|
||||||
|
[required]="!createProfile"
|
||||||
|
[transportType]="deviceWizardFormGroup.get('transportType').value"
|
||||||
|
formControlName="deviceProfileId"
|
||||||
|
(deviceProfileChanged)="$event?.transportType ? deviceWizardFormGroup.get('transportType').patchValue($event?.transportType) : {}"
|
||||||
|
[addNewProfile]="false"
|
||||||
|
[selectDefaultProfile]="true"
|
||||||
|
[editProfileEnabled]="false">
|
||||||
|
</tb-device-profile-autocomplete>
|
||||||
|
<mat-form-field fxFlex class="mat-block">
|
||||||
|
<mat-label translate>device-profile.new-device-profile-name</mat-label>
|
||||||
|
<input matInput formControlName="newDeviceProfileTitle"
|
||||||
|
[required]="createProfile">
|
||||||
|
<mat-error *ngIf="deviceWizardFormGroup.get('newDeviceProfileTitle').hasError('required')">
|
||||||
|
{{ 'device-profile.new-device-profile-name-required' | translate }}
|
||||||
|
</mat-error>
|
||||||
|
</mat-form-field>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
<mat-checkbox formControlName="gateway" style="padding-bottom: 16px;">
|
<mat-checkbox formControlName="gateway" style="padding-bottom: 16px;">
|
||||||
{{ 'device.is-gateway' | translate }}
|
{{ 'device.is-gateway' | translate }}
|
||||||
</mat-checkbox>
|
</mat-checkbox>
|
||||||
@ -69,39 +101,7 @@
|
|||||||
</fieldset>
|
</fieldset>
|
||||||
</form>
|
</form>
|
||||||
</mat-step>
|
</mat-step>
|
||||||
<mat-step [stepControl]="profileConfigFormGroup">
|
<mat-step [stepControl]="transportConfigFormGroup" *ngIf="createTransportConfiguration">
|
||||||
<form [formGroup]="profileConfigFormGroup" style="padding-bottom: 16px;">
|
|
||||||
<ng-template matStepLabel>{{ 'device.wizard.profile-configuration' | translate}}</ng-template>
|
|
||||||
<mat-radio-group fxLayout="column" fxFlex formControlName="addProfileType">
|
|
||||||
<mat-radio-button [value]="0" color="primary">
|
|
||||||
<section>
|
|
||||||
<span translate>device.wizard.existing-device-profile</span>
|
|
||||||
<tb-device-profile-autocomplete
|
|
||||||
[required]="profileConfigFormGroup.get('addProfileType').value === 0"
|
|
||||||
[transportType]="deviceWizardFormGroup.get('transportType').value"
|
|
||||||
formControlName="deviceProfileId"
|
|
||||||
[addNewProfile]="false"
|
|
||||||
[editProfileEnabled]="false">
|
|
||||||
</tb-device-profile-autocomplete>
|
|
||||||
</section>
|
|
||||||
</mat-radio-button>
|
|
||||||
<mat-radio-button [value]="1" color="primary">
|
|
||||||
<section fxLayout="column">
|
|
||||||
<span translate>device.wizard.new-device-profile</span>
|
|
||||||
<mat-form-field fxFlex class="mat-block">
|
|
||||||
<mat-label translate>device-profile.device-profile</mat-label>
|
|
||||||
<input matInput formControlName="newDeviceProfileTitle"
|
|
||||||
[required]="profileConfigFormGroup.get('addProfileType').value === 1">
|
|
||||||
<mat-error *ngIf="profileConfigFormGroup.get('newDeviceProfileTitle').hasError('required')">
|
|
||||||
{{ 'device-profile.device-profile-required' | translate }}
|
|
||||||
</mat-error>
|
|
||||||
</mat-form-field>
|
|
||||||
</section>
|
|
||||||
</mat-radio-button>
|
|
||||||
</mat-radio-group>
|
|
||||||
</form>
|
|
||||||
</mat-step>
|
|
||||||
<mat-step [stepControl]="transportConfigFormGroup" *ngIf="createdProfile">
|
|
||||||
<form [formGroup]="transportConfigFormGroup" style="padding-bottom: 16px;">
|
<form [formGroup]="transportConfigFormGroup" style="padding-bottom: 16px;">
|
||||||
<ng-template matStepLabel>{{ 'device-profile.transport-configuration' | translate }}</ng-template>
|
<ng-template matStepLabel>{{ 'device-profile.transport-configuration' | translate }}</ng-template>
|
||||||
<tb-device-profile-transport-configuration
|
<tb-device-profile-transport-configuration
|
||||||
@ -110,9 +110,9 @@
|
|||||||
</tb-device-profile-transport-configuration>
|
</tb-device-profile-transport-configuration>
|
||||||
</form>
|
</form>
|
||||||
</mat-step>
|
</mat-step>
|
||||||
<mat-step [stepControl]="alarmRulesFormGroup" *ngIf="createdProfile">
|
<mat-step [stepControl]="alarmRulesFormGroup" [optional]="true" *ngIf="createProfile">
|
||||||
<form [formGroup]="alarmRulesFormGroup" style="padding-bottom: 16px;">
|
<form [formGroup]="alarmRulesFormGroup" style="padding-bottom: 16px;">
|
||||||
<ng-template matStepLabel>{{'device-profile.alarm-rules' | translate:
|
<ng-template matStepLabel>{{'device-profile.alarm-rules-with-count' | translate:
|
||||||
{count: alarmRulesFormGroup.get('alarms').value ?
|
{count: alarmRulesFormGroup.get('alarms').value ?
|
||||||
alarmRulesFormGroup.get('alarms').value.length : 0} }}</ng-template>
|
alarmRulesFormGroup.get('alarms').value.length : 0} }}</ng-template>
|
||||||
<tb-device-profile-alarms
|
<tb-device-profile-alarms
|
||||||
@ -120,36 +120,50 @@
|
|||||||
</tb-device-profile-alarms>
|
</tb-device-profile-alarms>
|
||||||
</form>
|
</form>
|
||||||
</mat-step>
|
</mat-step>
|
||||||
<mat-step [stepControl]="specificConfigFormGroup">
|
<mat-step [stepControl]="credentialsFormGroup" [optional]="true">
|
||||||
<ng-template matStepLabel>{{ 'device.wizard.specific-configuration' | translate }}</ng-template>
|
<ng-template matStepLabel>{{ 'device.credentials' | translate }}</ng-template>
|
||||||
<form [formGroup]="specificConfigFormGroup" style="padding-bottom: 16px;">
|
<form [formGroup]="credentialsFormGroup" style="padding-bottom: 16px;">
|
||||||
|
<mat-checkbox style="padding-bottom: 16px;" formControlName="setCredential">{{ 'device.wizard.add-credential' | translate }}</mat-checkbox>
|
||||||
|
<tb-device-credentials
|
||||||
|
[fxShow]="credentialsFormGroup.get('setCredential').value"
|
||||||
|
formControlName="credential">
|
||||||
|
</tb-device-credentials>
|
||||||
|
</form>
|
||||||
|
</mat-step>
|
||||||
|
<mat-step [stepControl]="customerFormGroup" [optional]="true">
|
||||||
|
<ng-template matStepLabel>{{ 'customer.customer' | translate }}</ng-template>
|
||||||
|
<form [formGroup]="customerFormGroup" style="padding-bottom: 16px;">
|
||||||
<tb-entity-autocomplete
|
<tb-entity-autocomplete
|
||||||
formControlName="customerId"
|
formControlName="customerId"
|
||||||
labelText="device.wizard.customer-to-assign-device"
|
labelText="device.wizard.customer-to-assign-device"
|
||||||
[entityType]="entityType.CUSTOMER">
|
[entityType]="entityType.CUSTOMER">
|
||||||
</tb-entity-autocomplete>
|
</tb-entity-autocomplete>
|
||||||
<mat-checkbox formControlName="setCredential">{{ 'device.wizard.add-credential' | translate }}</mat-checkbox>
|
|
||||||
<tb-device-credentials
|
|
||||||
[fxShow]="specificConfigFormGroup.get('setCredential').value"
|
|
||||||
formControlName="credential">
|
|
||||||
</tb-device-credentials>
|
|
||||||
</form>
|
</form>
|
||||||
</mat-step>
|
</mat-step>
|
||||||
</mat-horizontal-stepper>
|
</mat-horizontal-stepper>
|
||||||
</div>
|
</div>
|
||||||
<div mat-dialog-actions fxLayout="row wrap" fxLayoutAlign="space-between center">
|
<div mat-dialog-actions fxLayout="column" fxLayoutAlign="start wrap" fxLayoutGap="8px" style="height: 100px;">
|
||||||
<button mat-button *ngIf="selectedIndex > 0"
|
<div fxFlex fxLayout="row" fxLayoutAlign="end">
|
||||||
|
<button mat-raised-button
|
||||||
|
*ngIf="showNext"
|
||||||
[disabled]="(isLoading$ | async)"
|
[disabled]="(isLoading$ | async)"
|
||||||
(click)="previousStep()">{{ 'action.back' | translate }}</button>
|
(click)="nextStep()">{{ 'action.next-with-label' | translate:{label: (getFormLabel(this.selectedIndex+1) | translate)} }}</button>
|
||||||
<span *ngIf="selectedIndex == 0"></span>
|
</div>
|
||||||
<div fxLayout="row wrap" fxLayoutGap="20px">
|
<div fxFlex fxLayout="row">
|
||||||
<button mat-button
|
<button mat-button
|
||||||
|
color="primary"
|
||||||
[disabled]="(isLoading$ | async)"
|
[disabled]="(isLoading$ | async)"
|
||||||
(click)="cancel()">{{ 'action.cancel' | translate }}</button>
|
(click)="cancel()">{{ 'action.cancel' | translate }}</button>
|
||||||
|
<span fxFlex></span>
|
||||||
|
<div fxLayout="row wrap" fxLayoutGap="8px">
|
||||||
|
<button mat-raised-button *ngIf="selectedIndex > 0"
|
||||||
|
[disabled]="(isLoading$ | async)"
|
||||||
|
(click)="previousStep()">{{ 'action.back' | translate }}</button>
|
||||||
<button mat-raised-button
|
<button mat-raised-button
|
||||||
[disabled]="(isLoading$ | async) || selectedForm.invalid"
|
[disabled]="(isLoading$ | async)"
|
||||||
color="primary"
|
color="primary"
|
||||||
(click)="nextStep()">{{ nextStepButtonLabel$ | async | translate }}</button>
|
(click)="add()">{{ 'action.add' | translate }}</button>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@ -13,25 +13,51 @@
|
|||||||
* See the License for the specific language governing permissions and
|
* See the License for the specific language governing permissions and
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
:host {
|
|
||||||
.mat-dialog-content {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
overflow: hidden;
|
|
||||||
|
|
||||||
.mat-stepper-horizontal {
|
@import "../../../../../scss/constants";
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
:host-context(.tb-fullscreen-dialog .mat-dialog-container) {
|
||||||
overflow: hidden;
|
@media #{$mat-lt-sm} {
|
||||||
|
.mat-dialog-content {
|
||||||
|
max-height: 75vh;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
:host ::ng-deep {
|
:host ::ng-deep {
|
||||||
.mat-dialog-content {
|
.mat-dialog-content {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
height: 100%;
|
||||||
|
|
||||||
.mat-stepper-horizontal {
|
.mat-stepper-horizontal {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
height: 100%;
|
||||||
|
overflow: hidden;
|
||||||
|
@media #{$mat-lt-sm} {
|
||||||
|
.mat-step-label {
|
||||||
|
white-space: normal;
|
||||||
|
overflow: visible;
|
||||||
|
.mat-step-text-label {
|
||||||
|
overflow: visible;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
.mat-horizontal-content-container {
|
.mat-horizontal-content-container {
|
||||||
overflow: auto;
|
height: 450px;
|
||||||
|
max-height: 100%;
|
||||||
|
width: 100%;;
|
||||||
|
overflow-y: auto;
|
||||||
|
@media #{$mat-gt-sm} {
|
||||||
|
min-width: 800px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.mat-horizontal-stepper-content[aria-expanded=true] {
|
||||||
|
height: 100%;
|
||||||
|
form {
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -26,7 +26,7 @@ import {
|
|||||||
createDeviceProfileTransportConfiguration,
|
createDeviceProfileTransportConfiguration,
|
||||||
DeviceProfile,
|
DeviceProfile,
|
||||||
DeviceProfileType,
|
DeviceProfileType,
|
||||||
DeviceTransportType,
|
DeviceTransportType, deviceTransportTypeHintMap,
|
||||||
deviceTransportTypeTranslationMap
|
deviceTransportTypeTranslationMap
|
||||||
} from '@shared/models/device.models';
|
} from '@shared/models/device.models';
|
||||||
import { MatHorizontalStepper } from '@angular/material/stepper';
|
import { MatHorizontalStepper } from '@angular/material/stepper';
|
||||||
@ -35,11 +35,13 @@ import { BaseData, HasId } from '@shared/models/base-data';
|
|||||||
import { EntityType } from '@shared/models/entity-type.models';
|
import { EntityType } from '@shared/models/entity-type.models';
|
||||||
import { DeviceProfileService } from '@core/http/device-profile.service';
|
import { DeviceProfileService } from '@core/http/device-profile.service';
|
||||||
import { EntityId } from '@shared/models/id/entity-id';
|
import { EntityId } from '@shared/models/id/entity-id';
|
||||||
import { BehaviorSubject, Observable, of, Subscription } from 'rxjs';
|
import { Observable, of, Subscription } from 'rxjs';
|
||||||
import { map, mergeMap, tap } from 'rxjs/operators';
|
import { map, mergeMap, tap } from 'rxjs/operators';
|
||||||
import { DeviceService } from '@core/http/device.service';
|
import { DeviceService } from '@core/http/device.service';
|
||||||
import { ErrorStateMatcher } from '@angular/material/core';
|
import { ErrorStateMatcher } from '@angular/material/core';
|
||||||
import { StepperSelectionEvent } from '@angular/cdk/stepper';
|
import { StepperSelectionEvent } from '@angular/cdk/stepper';
|
||||||
|
import { BreakpointObserver, BreakpointState } from '@angular/cdk/layout';
|
||||||
|
import { MediaBreakpoints } from '@shared/models/constants';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'tb-device-wizard',
|
selector: 'tb-device-wizard',
|
||||||
@ -54,9 +56,10 @@ export class DeviceWizardDialogComponent extends
|
|||||||
|
|
||||||
selectedIndex = 0;
|
selectedIndex = 0;
|
||||||
|
|
||||||
nextStepButtonLabel$ = new BehaviorSubject<string>('action.continue');
|
showNext = true;
|
||||||
|
|
||||||
createdProfile = false;
|
createProfile = false;
|
||||||
|
createTransportConfiguration = false;
|
||||||
|
|
||||||
entityType = EntityType;
|
entityType = EntityType;
|
||||||
|
|
||||||
@ -64,15 +67,19 @@ export class DeviceWizardDialogComponent extends
|
|||||||
|
|
||||||
deviceTransportTypeTranslations = deviceTransportTypeTranslationMap;
|
deviceTransportTypeTranslations = deviceTransportTypeTranslationMap;
|
||||||
|
|
||||||
deviceWizardFormGroup: FormGroup;
|
deviceTransportTypeHints = deviceTransportTypeHintMap;
|
||||||
|
|
||||||
profileConfigFormGroup: FormGroup;
|
deviceWizardFormGroup: FormGroup;
|
||||||
|
|
||||||
transportConfigFormGroup: FormGroup;
|
transportConfigFormGroup: FormGroup;
|
||||||
|
|
||||||
alarmRulesFormGroup: FormGroup;
|
alarmRulesFormGroup: FormGroup;
|
||||||
|
|
||||||
specificConfigFormGroup: FormGroup;
|
credentialsFormGroup: FormGroup;
|
||||||
|
|
||||||
|
customerFormGroup: FormGroup;
|
||||||
|
|
||||||
|
labelPosition = 'end';
|
||||||
|
|
||||||
private subscriptions: Subscription[] = [];
|
private subscriptions: Subscription[] = [];
|
||||||
|
|
||||||
@ -83,6 +90,7 @@ export class DeviceWizardDialogComponent extends
|
|||||||
public dialogRef: MatDialogRef<DeviceWizardDialogComponent, boolean>,
|
public dialogRef: MatDialogRef<DeviceWizardDialogComponent, boolean>,
|
||||||
private deviceProfileService: DeviceProfileService,
|
private deviceProfileService: DeviceProfileService,
|
||||||
private deviceService: DeviceService,
|
private deviceService: DeviceService,
|
||||||
|
private breakpointObserver: BreakpointObserver,
|
||||||
private fb: FormBuilder) {
|
private fb: FormBuilder) {
|
||||||
super(store, router, dialogRef);
|
super(store, router, dialogRef);
|
||||||
this.deviceWizardFormGroup = this.fb.group({
|
this.deviceWizardFormGroup = this.fb.group({
|
||||||
@ -90,33 +98,32 @@ export class DeviceWizardDialogComponent extends
|
|||||||
label: [''],
|
label: [''],
|
||||||
gateway: [false],
|
gateway: [false],
|
||||||
transportType: [DeviceTransportType.DEFAULT, Validators.required],
|
transportType: [DeviceTransportType.DEFAULT, Validators.required],
|
||||||
|
addProfileType: [0],
|
||||||
|
deviceProfileId: [null, Validators.required],
|
||||||
|
newDeviceProfileTitle: [{value: null, disabled: true}],
|
||||||
description: ['']
|
description: ['']
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
this.profileConfigFormGroup = this.fb.group({
|
this.subscriptions.push(this.deviceWizardFormGroup.get('addProfileType').valueChanges.subscribe(
|
||||||
addProfileType: [0],
|
|
||||||
deviceProfileId: [null, Validators.required],
|
|
||||||
newDeviceProfileTitle: [{value: null, disabled: true}]
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
this.subscriptions.push(this.profileConfigFormGroup.get('addProfileType').valueChanges.subscribe(
|
|
||||||
(addProfileType: number) => {
|
(addProfileType: number) => {
|
||||||
if (addProfileType === 0) {
|
if (addProfileType === 0) {
|
||||||
this.profileConfigFormGroup.get('deviceProfileId').setValidators([Validators.required]);
|
this.deviceWizardFormGroup.get('deviceProfileId').setValidators([Validators.required]);
|
||||||
this.profileConfigFormGroup.get('deviceProfileId').enable();
|
this.deviceWizardFormGroup.get('deviceProfileId').enable();
|
||||||
this.profileConfigFormGroup.get('newDeviceProfileTitle').setValidators(null);
|
this.deviceWizardFormGroup.get('newDeviceProfileTitle').setValidators(null);
|
||||||
this.profileConfigFormGroup.get('newDeviceProfileTitle').disable();
|
this.deviceWizardFormGroup.get('newDeviceProfileTitle').disable();
|
||||||
this.profileConfigFormGroup.updateValueAndValidity();
|
this.deviceWizardFormGroup.updateValueAndValidity();
|
||||||
this.createdProfile = false;
|
this.createProfile = false;
|
||||||
|
this.createTransportConfiguration = false;
|
||||||
} else {
|
} else {
|
||||||
this.profileConfigFormGroup.get('deviceProfileId').setValidators(null);
|
this.deviceWizardFormGroup.get('deviceProfileId').setValidators(null);
|
||||||
this.profileConfigFormGroup.get('deviceProfileId').disable();
|
this.deviceWizardFormGroup.get('deviceProfileId').disable();
|
||||||
this.profileConfigFormGroup.get('newDeviceProfileTitle').setValidators([Validators.required]);
|
this.deviceWizardFormGroup.get('newDeviceProfileTitle').setValidators([Validators.required]);
|
||||||
this.profileConfigFormGroup.get('newDeviceProfileTitle').enable();
|
this.deviceWizardFormGroup.get('newDeviceProfileTitle').enable();
|
||||||
this.profileConfigFormGroup.updateValueAndValidity();
|
this.deviceWizardFormGroup.updateValueAndValidity();
|
||||||
this.createdProfile = true;
|
this.createProfile = true;
|
||||||
|
this.createTransportConfiguration = this.deviceWizardFormGroup.get('transportType').value &&
|
||||||
|
DeviceTransportType.DEFAULT !== this.deviceWizardFormGroup.get('transportType').value;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
));
|
));
|
||||||
@ -135,20 +142,37 @@ export class DeviceWizardDialogComponent extends
|
|||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
this.specificConfigFormGroup = this.fb.group({
|
this.credentialsFormGroup = this.fb.group({
|
||||||
customerId: [null],
|
|
||||||
setCredential: [false],
|
setCredential: [false],
|
||||||
credential: [{value: null, disabled: true}]
|
credential: [{value: null, disabled: true}]
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
this.subscriptions.push(this.specificConfigFormGroup.get('setCredential').valueChanges.subscribe((value) => {
|
this.subscriptions.push(this.credentialsFormGroup.get('setCredential').valueChanges.subscribe((value) => {
|
||||||
if (value) {
|
if (value) {
|
||||||
this.specificConfigFormGroup.get('credential').enable();
|
this.credentialsFormGroup.get('credential').enable();
|
||||||
} else {
|
} else {
|
||||||
this.specificConfigFormGroup.get('credential').disable();
|
this.credentialsFormGroup.get('credential').disable();
|
||||||
}
|
}
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
this.customerFormGroup = this.fb.group({
|
||||||
|
customerId: [null]
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
this.labelPosition = this.breakpointObserver.isMatched(MediaBreakpoints['gt-sm']) ? 'end' : 'bottom';
|
||||||
|
|
||||||
|
this.subscriptions.push(this.breakpointObserver
|
||||||
|
.observe(MediaBreakpoints['gt-sm'])
|
||||||
|
.subscribe((state: BreakpointState) => {
|
||||||
|
if (state.matches) {
|
||||||
|
this.labelPosition = 'end';
|
||||||
|
} else {
|
||||||
|
this.labelPosition = 'bottom';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
ngOnDestroy() {
|
ngOnDestroy() {
|
||||||
@ -171,26 +195,28 @@ export class DeviceWizardDialogComponent extends
|
|||||||
}
|
}
|
||||||
|
|
||||||
nextStep(): void {
|
nextStep(): void {
|
||||||
if (this.selectedIndex < this.maxStepperIndex) {
|
|
||||||
this.addDeviceWizardStepper.next();
|
this.addDeviceWizardStepper.next();
|
||||||
} else {
|
|
||||||
this.add();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
get selectedForm(): FormGroup {
|
getFormLabel(index: number): string {
|
||||||
const index = !this.createdProfile && this.selectedIndex === this.maxStepperIndex ? 4 : this.selectedIndex;
|
if (index > 0) {
|
||||||
|
if (!this.createProfile) {
|
||||||
|
index += 2;
|
||||||
|
} else if (!this.createTransportConfiguration) {
|
||||||
|
index += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
switch (index) {
|
switch (index) {
|
||||||
case 0:
|
case 0:
|
||||||
return this.deviceWizardFormGroup;
|
return 'device.wizard.device-details';
|
||||||
case 1:
|
case 1:
|
||||||
return this.profileConfigFormGroup;
|
return 'device-profile.transport-configuration';
|
||||||
case 2:
|
case 2:
|
||||||
return this.transportConfigFormGroup;
|
return 'device-profile.alarm-rules';
|
||||||
case 3:
|
case 3:
|
||||||
return this.alarmRulesFormGroup;
|
return 'device.credentials';
|
||||||
case 4:
|
case 4:
|
||||||
return this.specificConfigFormGroup;
|
return 'customer.customer';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -201,23 +227,27 @@ export class DeviceWizardDialogComponent extends
|
|||||||
private deviceProfileTransportTypeChanged(deviceTransportType: DeviceTransportType): void {
|
private deviceProfileTransportTypeChanged(deviceTransportType: DeviceTransportType): void {
|
||||||
this.transportConfigFormGroup.patchValue(
|
this.transportConfigFormGroup.patchValue(
|
||||||
{transportConfiguration: createDeviceProfileTransportConfiguration(deviceTransportType)});
|
{transportConfiguration: createDeviceProfileTransportConfiguration(deviceTransportType)});
|
||||||
|
this.createTransportConfiguration = this.createProfile && deviceTransportType &&
|
||||||
|
DeviceTransportType.DEFAULT !== deviceTransportType;
|
||||||
}
|
}
|
||||||
|
|
||||||
private add(): void {
|
add(): void {
|
||||||
this.creatProfile().pipe(
|
if (this.allValid()) {
|
||||||
mergeMap(profileId => this.createdDevice(profileId)),
|
this.createDeviceProfile().pipe(
|
||||||
mergeMap(device => this.saveCredential(device))
|
mergeMap(profileId => this.createDevice(profileId)),
|
||||||
|
mergeMap(device => this.saveCredentials(device))
|
||||||
).subscribe(
|
).subscribe(
|
||||||
(created) => {
|
(created) => {
|
||||||
this.dialogRef.close(created);
|
this.dialogRef.close(created);
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private creatProfile(): Observable<EntityId> {
|
private createDeviceProfile(): Observable<EntityId> {
|
||||||
if (this.profileConfigFormGroup.get('addProfileType').value) {
|
if (this.deviceWizardFormGroup.get('addProfileType').value) {
|
||||||
const deviceProfile: DeviceProfile = {
|
const deviceProfile: DeviceProfile = {
|
||||||
name: this.profileConfigFormGroup.get('newDeviceProfileTitle').value,
|
name: this.deviceWizardFormGroup.get('newDeviceProfileTitle').value,
|
||||||
type: DeviceProfileType.DEFAULT,
|
type: DeviceProfileType.DEFAULT,
|
||||||
transportType: this.deviceWizardFormGroup.get('transportType').value,
|
transportType: this.deviceWizardFormGroup.get('transportType').value,
|
||||||
profileData: {
|
profileData: {
|
||||||
@ -229,11 +259,10 @@ export class DeviceWizardDialogComponent extends
|
|||||||
return this.deviceProfileService.saveDeviceProfile(deviceProfile).pipe(
|
return this.deviceProfileService.saveDeviceProfile(deviceProfile).pipe(
|
||||||
map(profile => profile.id),
|
map(profile => profile.id),
|
||||||
tap((profileId) => {
|
tap((profileId) => {
|
||||||
this.profileConfigFormGroup.patchValue({
|
this.deviceWizardFormGroup.patchValue({
|
||||||
deviceProfileId: profileId,
|
deviceProfileId: profileId,
|
||||||
addProfileType: 0
|
addProfileType: 0
|
||||||
});
|
});
|
||||||
this.addDeviceWizardStepper.selectedIndex = 2;
|
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
@ -241,7 +270,7 @@ export class DeviceWizardDialogComponent extends
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private createdDevice(profileId: EntityId = this.profileConfigFormGroup.get('deviceProfileId').value): Observable<BaseData<HasId>> {
|
private createDevice(profileId: EntityId = this.deviceWizardFormGroup.get('deviceProfileId').value): Observable<BaseData<HasId>> {
|
||||||
const device = {
|
const device = {
|
||||||
name: this.deviceWizardFormGroup.get('name').value,
|
name: this.deviceWizardFormGroup.get('name').value,
|
||||||
label: this.deviceWizardFormGroup.get('label').value,
|
label: this.deviceWizardFormGroup.get('label').value,
|
||||||
@ -252,21 +281,21 @@ export class DeviceWizardDialogComponent extends
|
|||||||
},
|
},
|
||||||
customerId: null
|
customerId: null
|
||||||
};
|
};
|
||||||
if (this.specificConfigFormGroup.get('customerId').value) {
|
if (this.customerFormGroup.get('customerId').value) {
|
||||||
device.customerId = {
|
device.customerId = {
|
||||||
entityType: EntityType.CUSTOMER,
|
entityType: EntityType.CUSTOMER,
|
||||||
id: this.specificConfigFormGroup.get('customerId').value
|
id: this.customerFormGroup.get('customerId').value
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
return this.data.entitiesTableConfig.saveEntity(device);
|
return this.data.entitiesTableConfig.saveEntity(device);
|
||||||
}
|
}
|
||||||
|
|
||||||
private saveCredential(device: BaseData<HasId>): Observable<boolean> {
|
private saveCredentials(device: BaseData<HasId>): Observable<boolean> {
|
||||||
if (this.specificConfigFormGroup.get('setCredential').value) {
|
if (this.credentialsFormGroup.get('setCredential').value) {
|
||||||
return this.deviceService.getDeviceCredentials(device.id.id).pipe(
|
return this.deviceService.getDeviceCredentials(device.id.id).pipe(
|
||||||
mergeMap(
|
mergeMap(
|
||||||
(deviceCredentials) => {
|
(deviceCredentials) => {
|
||||||
const deviceCredentialsValue = {...deviceCredentials, ...this.specificConfigFormGroup.value.credential};
|
const deviceCredentialsValue = {...deviceCredentials, ...this.credentialsFormGroup.value.credential};
|
||||||
return this.deviceService.saveDeviceCredentials(deviceCredentialsValue);
|
return this.deviceService.saveDeviceCredentials(deviceCredentialsValue);
|
||||||
}
|
}
|
||||||
),
|
),
|
||||||
@ -275,12 +304,28 @@ export class DeviceWizardDialogComponent extends
|
|||||||
return of(true);
|
return of(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
allValid(): boolean {
|
||||||
|
if (this.addDeviceWizardStepper.steps.find((item, index) => {
|
||||||
|
if (item.stepControl.invalid) {
|
||||||
|
item.interacted = true;
|
||||||
|
this.addDeviceWizardStepper.selectedIndex = index;
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
} )) {
|
||||||
|
return false;
|
||||||
|
} else {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
changeStep($event: StepperSelectionEvent): void {
|
changeStep($event: StepperSelectionEvent): void {
|
||||||
this.selectedIndex = $event.selectedIndex;
|
this.selectedIndex = $event.selectedIndex;
|
||||||
if (this.selectedIndex === this.maxStepperIndex) {
|
if (this.selectedIndex === this.maxStepperIndex) {
|
||||||
this.nextStepButtonLabel$.next('action.add');
|
this.showNext = false;
|
||||||
} else {
|
} else {
|
||||||
this.nextStepButtonLabel$.next('action.continue');
|
this.showNext = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -296,7 +296,7 @@ export class DevicesTableConfigResolver implements Resolve<EntityTableConfig<Dev
|
|||||||
name: this.translate.instant('device.add-device-text'),
|
name: this.translate.instant('device.add-device-text'),
|
||||||
icon: 'insert_drive_file',
|
icon: 'insert_drive_file',
|
||||||
isEnabled: () => true,
|
isEnabled: () => true,
|
||||||
onAction: ($event) => this.config.table.addEntity($event)
|
onAction: ($event) => this.deviceWizard($event)
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: this.translate.instant('device.import'),
|
name: this.translate.instant('device.import'),
|
||||||
@ -304,12 +304,6 @@ export class DevicesTableConfigResolver implements Resolve<EntityTableConfig<Dev
|
|||||||
isEnabled: () => true,
|
isEnabled: () => true,
|
||||||
onAction: ($event) => this.importDevices($event)
|
onAction: ($event) => this.importDevices($event)
|
||||||
},
|
},
|
||||||
{
|
|
||||||
name: this.translate.instant('device.wizard.device-wizard'),
|
|
||||||
icon: 'library_add',
|
|
||||||
isEnabled: () => true,
|
|
||||||
onAction: ($event) => this.deviceWizard($event)
|
|
||||||
},
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
if (deviceScope === 'customer') {
|
if (deviceScope === 'customer') {
|
||||||
|
|||||||
@ -72,6 +72,14 @@ export const deviceTransportTypeTranslationMap = new Map<DeviceTransportType, st
|
|||||||
]
|
]
|
||||||
);
|
);
|
||||||
|
|
||||||
|
export const deviceTransportTypeHintMap = new Map<DeviceTransportType, string>(
|
||||||
|
[
|
||||||
|
[DeviceTransportType.DEFAULT, 'device-profile.transport-type-default-hint'],
|
||||||
|
[DeviceTransportType.MQTT, 'device-profile.transport-type-mqtt-hint'],
|
||||||
|
[DeviceTransportType.LWM2M, 'device-profile.transport-type-lwm2m-hint']
|
||||||
|
]
|
||||||
|
);
|
||||||
|
|
||||||
export const mqttTransportPayloadTypeTranslationMap = new Map<MqttTransportPayloadType, string>(
|
export const mqttTransportPayloadTypeTranslationMap = new Map<MqttTransportPayloadType, string>(
|
||||||
[
|
[
|
||||||
[MqttTransportPayloadType.JSON, 'device-profile.mqtt-device-payload-type-json'],
|
[MqttTransportPayloadType.JSON, 'device-profile.mqtt-device-payload-type-json'],
|
||||||
|
|||||||
@ -54,7 +54,8 @@
|
|||||||
"share-via": "Share via {{provider}}",
|
"share-via": "Share via {{provider}}",
|
||||||
"continue": "Continue",
|
"continue": "Continue",
|
||||||
"discard-changes": "Discard Changes",
|
"discard-changes": "Discard Changes",
|
||||||
"download": "Download"
|
"download": "Download",
|
||||||
|
"next-with-label": "Next: {{label}}"
|
||||||
},
|
},
|
||||||
"aggregation": {
|
"aggregation": {
|
||||||
"aggregation": "Aggregation",
|
"aggregation": "Aggregation",
|
||||||
@ -760,8 +761,7 @@
|
|||||||
"wizard": {
|
"wizard": {
|
||||||
"device-wizard": "Device Wizard",
|
"device-wizard": "Device Wizard",
|
||||||
"device-details": "Device details",
|
"device-details": "Device details",
|
||||||
"profile-configuration": "Profile configuration",
|
"new-device-profile": "Create new device profile",
|
||||||
"new-device-profile": "New device profile",
|
|
||||||
"existing-device-profile": "Select existing device profile",
|
"existing-device-profile": "Select existing device profile",
|
||||||
"specific-configuration": "Specific configuration",
|
"specific-configuration": "Specific configuration",
|
||||||
"customer-to-assign-device": "Customer to assign the device",
|
"customer-to-assign-device": "Customer to assign the device",
|
||||||
@ -784,6 +784,8 @@
|
|||||||
"set-default": "Make device profile default",
|
"set-default": "Make device profile default",
|
||||||
"delete": "Delete device profile",
|
"delete": "Delete device profile",
|
||||||
"copyId": "Copy device profile Id",
|
"copyId": "Copy device profile Id",
|
||||||
|
"new-device-profile-name": "Device profile name",
|
||||||
|
"new-device-profile-name-required": "Device profile name is required.",
|
||||||
"name": "Name",
|
"name": "Name",
|
||||||
"name-required": "Name is required.",
|
"name-required": "Name is required.",
|
||||||
"type": "Profile type",
|
"type": "Profile type",
|
||||||
@ -792,8 +794,11 @@
|
|||||||
"transport-type": "Transport type",
|
"transport-type": "Transport type",
|
||||||
"transport-type-required": "Transport type is required.",
|
"transport-type-required": "Transport type is required.",
|
||||||
"transport-type-default": "Default",
|
"transport-type-default": "Default",
|
||||||
|
"transport-type-default-hint": "Default transport type",
|
||||||
"transport-type-mqtt": "MQTT",
|
"transport-type-mqtt": "MQTT",
|
||||||
|
"transport-type-mqtt-hint": "MQTT transport type",
|
||||||
"transport-type-lwm2m": "LWM2M",
|
"transport-type-lwm2m": "LWM2M",
|
||||||
|
"transport-type-lwm2m-hint": "LWM2M transport type",
|
||||||
"description": "Description",
|
"description": "Description",
|
||||||
"default": "Default",
|
"default": "Default",
|
||||||
"profile-configuration": "Profile configuration",
|
"profile-configuration": "Profile configuration",
|
||||||
@ -824,7 +829,8 @@
|
|||||||
"not-valid-multi-character": "Invalid use of a multi-level wildcard character",
|
"not-valid-multi-character": "Invalid use of a multi-level wildcard character",
|
||||||
"single-level-wildcards-hint": "<code>[+]</code> is suitable for any topic filter level. Ex.: <b>v1/devices/+/telemetry</b> or <b>+/devices/+/attributes</b>.",
|
"single-level-wildcards-hint": "<code>[+]</code> is suitable for any topic filter level. Ex.: <b>v1/devices/+/telemetry</b> or <b>+/devices/+/attributes</b>.",
|
||||||
"multi-level-wildcards-hint": "<code>[#]</code> can replace the topic filter itself and must be the last symbol of the topic. Ex.: <b>#</b> or <b>v1/devices/me/#</b>.",
|
"multi-level-wildcards-hint": "<code>[#]</code> can replace the topic filter itself and must be the last symbol of the topic. Ex.: <b>#</b> or <b>v1/devices/me/#</b>.",
|
||||||
"alarm-rules": "Alarm rules ({{count}})",
|
"alarm-rules": "Alarm rules",
|
||||||
|
"alarm-rules-with-count": "Alarm rules ({{count}})",
|
||||||
"no-alarm-rules": "No alarm rules configured",
|
"no-alarm-rules": "No alarm rules configured",
|
||||||
"add-alarm-rule": "Add alarm rule",
|
"add-alarm-rule": "Add alarm rule",
|
||||||
"edit-alarm-rule": "Edit alarm rule",
|
"edit-alarm-rule": "Edit alarm rule",
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user