Browse Source

UI add device wizard

pull/3523/head
Vladyslav_Prykhodko 6 years ago
parent
commit
f2af7d0831
  1. 12
      ui-ngx/src/app/core/http/device-profile.service.ts
  2. 3
      ui-ngx/src/app/modules/home/components/home-components.module.ts
  3. 3
      ui-ngx/src/app/modules/home/components/profile/add-device-profile-dialog.component.ts
  4. 8
      ui-ngx/src/app/modules/home/components/profile/device-profile-autocomplete.component.html
  5. 35
      ui-ngx/src/app/modules/home/components/profile/device-profile-autocomplete.component.ts
  6. 155
      ui-ngx/src/app/modules/home/components/wizard/device-wizard-dialog.component.html
  7. 38
      ui-ngx/src/app/modules/home/components/wizard/device-wizard-dialog.component.scss
  8. 286
      ui-ngx/src/app/modules/home/components/wizard/device-wizard-dialog.component.ts
  9. 3
      ui-ngx/src/app/modules/home/pages/device-profile/device-profiles-table-config.resolver.ts
  10. 36
      ui-ngx/src/app/modules/home/pages/device/devices-table-config.resolver.ts
  11. 12
      ui-ngx/src/assets/locale/locale.constant-en_US.json

12
ui-ngx/src/app/core/http/device-profile.service.ts

@ -20,7 +20,8 @@ import { PageLink } from '@shared/models/page/page-link';
import { defaultHttpOptionsFromConfig, RequestConfig } from './http-utils';
import { Observable } from 'rxjs';
import { PageData } from '@shared/models/page/page-data';
import { DeviceProfile, DeviceProfileInfo } from '@shared/models/device.models';
import { DeviceProfile, DeviceProfileInfo, DeviceTransportType } from '@shared/models/device.models';
import { isDefinedAndNotNull } from '@core/utils';
@Injectable({
providedIn: 'root'
@ -59,8 +60,13 @@ export class DeviceProfileService {
return this.http.get<DeviceProfileInfo>(`/api/deviceProfileInfo/${deviceProfileId}`, defaultHttpOptionsFromConfig(config));
}
public getDeviceProfileInfos(pageLink: PageLink, config?: RequestConfig): Observable<PageData<DeviceProfileInfo>> {
return this.http.get<PageData<DeviceProfileInfo>>(`/api/deviceProfileInfos${pageLink.toQuery()}`, defaultHttpOptionsFromConfig(config));
public getDeviceProfileInfos(pageLink: PageLink, transportType?: DeviceTransportType,
config?: RequestConfig): Observable<PageData<DeviceProfileInfo>> {
let url = `/api/deviceProfileInfos${pageLink.toQuery()}`;
if (isDefinedAndNotNull(transportType)) {
url += `&transportType=${transportType}`;
}
return this.http.get<PageData<DeviceProfileInfo>>(url, defaultHttpOptionsFromConfig(config));
}
}

3
ui-ngx/src/app/modules/home/components/home-components.module.ts

@ -107,6 +107,7 @@ import { AlarmRuleKeyFiltersDialogComponent } from './profile/alarm/alarm-rule-k
import { FilterTextComponent } from './filter/filter-text.component';
import { AddDeviceProfileDialogComponent } from './profile/add-device-profile-dialog.component';
import { RuleChainAutocompleteComponent } from './rule-chain/rule-chain-autocomplete.component';
import { DeviceWizardDialogComponent } from './wizard/device-wizard-dialog.component';
import { DeviceCredentialsComponent } from './device/device-credentials.component';
@NgModule({
@ -198,6 +199,7 @@ import { DeviceCredentialsComponent } from './device/device-credentials.componen
DeviceProfileDialogComponent,
AddDeviceProfileDialogComponent,
RuleChainAutocompleteComponent,
DeviceWizardDialogComponent,
DeviceCredentialsComponent
],
imports: [
@ -278,6 +280,7 @@ import { DeviceCredentialsComponent } from './device/device-credentials.componen
DeviceProfileDialogComponent,
AddDeviceProfileDialogComponent,
RuleChainAutocompleteComponent,
DeviceWizardDialogComponent,
DeviceCredentialsComponent
],
providers: [

3
ui-ngx/src/app/modules/home/components/profile/add-device-profile-dialog.component.ts

@ -46,6 +46,7 @@ import { RuleChainId } from '@shared/models/id/rule-chain-id';
export interface AddDeviceProfileDialogData {
deviceProfileName: string;
transportType: DeviceTransportType;
}
@Component({
@ -97,7 +98,7 @@ export class AddDeviceProfileDialogComponent extends
);
this.transportConfigFormGroup = this.fb.group(
{
transportType: [DeviceTransportType.DEFAULT, [Validators.required]],
transportType: [data.transportType ? data.transportType : DeviceTransportType.DEFAULT, [Validators.required]],
transportConfiguration: [createDeviceProfileTransportConfiguration(DeviceTransportType.DEFAULT),
[Validators.required]]
}

8
ui-ngx/src/app/modules/home/components/profile/device-profile-autocomplete.component.html

@ -48,7 +48,7 @@
</mat-option>
<mat-option *ngIf="!(filteredDeviceProfiles | async)?.length" [value]="null" class="tb-not-found">
<div class="tb-not-found-content" (click)="$event.stopPropagation()">
<div *ngIf="!textIsNotEmpty(searchText); else searchNotEmpty">
<div *ngIf="!textIsNotEmpty(searchText) || !addNewProfile; else searchNotEmpty">
<span translate>device-profile.no-device-profiles-found</span>
</div>
<ng-template #searchNotEmpty>
@ -56,10 +56,10 @@
{{ translate.get('device-profile.no-device-profiles-matching',
{entity: truncate.transform(searchText, true, 6, &apos;...&apos;)}) | async }}
</span>
<span>
<a translate (click)="createDeviceProfile($event, searchText)">device-profile.create-new-device-profile</a>
</span>
</ng-template>
<span>
<a translate (click)="createDeviceProfile($event, searchText)">device-profile.create-new-device-profile</a>
</span>
</div>
</mat-option>
</mat-autocomplete>

35
ui-ngx/src/app/modules/home/components/profile/device-profile-autocomplete.component.ts

@ -19,7 +19,8 @@ import {
ElementRef,
EventEmitter,
forwardRef,
Input, NgZone,
Input,
NgZone,
OnInit,
Output,
ViewChild
@ -38,14 +39,7 @@ import { TruncatePipe } from '@shared//pipe/truncate.pipe';
import { ENTER } from '@angular/cdk/keycodes';
import { MatDialog } from '@angular/material/dialog';
import { DeviceProfileId } from '@shared/models/id/device-profile-id';
import {
createDeviceProfileConfiguration,
createDeviceProfileTransportConfiguration,
DeviceProfile,
DeviceProfileInfo,
DeviceProfileType,
DeviceTransportType
} from '@shared/models/device.models';
import { DeviceProfile, DeviceProfileInfo, DeviceProfileType, DeviceTransportType } from '@shared/models/device.models';
import { DeviceProfileService } from '@core/http/device-profile.service';
import { DeviceProfileDialogComponent, DeviceProfileDialogData } from './device-profile-dialog.component';
import { MatAutocomplete } from '@angular/material/autocomplete';
@ -76,6 +70,12 @@ export class DeviceProfileAutocompleteComponent implements ControlValueAccessor,
@Input()
editProfileEnabled = true;
@Input()
addNewProfile = true;
@Input()
transportType: DeviceTransportType = null;
private requiredValue: boolean;
get required(): boolean {
return this.requiredValue;
@ -183,6 +183,11 @@ export class DeviceProfileAutocompleteComponent implements ControlValueAccessor,
setDisabledState(isDisabled: boolean): void {
this.disabled = isDisabled;
if (this.disabled) {
this.selectDeviceProfileFormGroup.disable();
} else {
this.selectDeviceProfileFormGroup.enable();
}
}
writeValue(value: DeviceProfileId | null): void {
@ -244,7 +249,7 @@ export class DeviceProfileAutocompleteComponent implements ControlValueAccessor,
property: 'name',
direction: Direction.ASC
});
return this.deviceProfileService.getDeviceProfileInfos(pageLink, {ignoreLoading: true}).pipe(
return this.deviceProfileService.getDeviceProfileInfos(pageLink, this.transportType, {ignoreLoading: true}).pipe(
map(pageData => {
let data = pageData.data;
if (this.displayAllOnEmpty) {
@ -280,9 +285,12 @@ export class DeviceProfileAutocompleteComponent implements ControlValueAccessor,
createDeviceProfile($event: Event, profileName: string) {
$event.preventDefault();
const deviceProfile: DeviceProfile = {
name: profileName
name: profileName,
transportType: this.transportType
} as DeviceProfile;
this.openDeviceProfileDialog(deviceProfile, true);
if (this.addNewProfile) {
this.openDeviceProfileDialog(deviceProfile, true);
}
}
editDeviceProfile($event: Event) {
@ -312,7 +320,8 @@ export class DeviceProfileAutocompleteComponent implements ControlValueAccessor,
disableClose: true,
panelClass: ['tb-dialog', 'tb-fullscreen-dialog'],
data: {
deviceProfileName: deviceProfile.name
deviceProfileName: deviceProfile.name,
transportType: deviceProfile.transportType
}
}).afterClosed();
}

155
ui-ngx/src/app/modules/home/components/wizard/device-wizard-dialog.component.html

@ -0,0 +1,155 @@
<!--
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 style="min-width: 1000px;">
<mat-toolbar color="primary">
<h2 translate>device.add-device-text</h2>
<span fxFlex></span>
<button mat-icon-button
(click)="cancel()"
type="button">
<mat-icon class="material-icons">close</mat-icon>
</button>
</mat-toolbar>
<mat-progress-bar color="warn" mode="indeterminate" *ngIf="isLoading$ | async">
</mat-progress-bar>
<div style="height: 4px;" *ngIf="!(isLoading$ | async)"></div>
<div mat-dialog-content>
<mat-horizontal-stepper [linear]="true" #addDeviceWizardStepper (selectionChange)="changeStep($event)">
<ng-template matStepperIcon="edit">
<mat-icon>check</mat-icon>
</ng-template>
<mat-step [stepControl]="deviceWizardFormGroup">
<form [formGroup]="deviceWizardFormGroup" style="padding-bottom: 16px;">
<ng-template matStepLabel>{{ 'device.wizard.device-details' | translate}}</ng-template>
<fieldset [disabled]="isLoading$ | async">
<mat-form-field class="mat-block">
<mat-label translate>device.name</mat-label>
<input matInput formControlName="name" required>
<mat-error *ngIf="deviceWizardFormGroup.get('name').hasError('required')">
{{ 'device.name-required' | translate }}
</mat-error>
</mat-form-field>
<mat-form-field class="mat-block">
<mat-label translate>device.label</mat-label>
<input matInput formControlName="label">
</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="deviceWizardFormGroup.get('transportType').hasError('required')">
{{ 'device-profile.transport-type-required' | translate }}
</mat-error>
</mat-form-field>
<mat-checkbox formControlName="gateway" style="padding-bottom: 16px;">
{{ 'device.is-gateway' | translate }}
</mat-checkbox>
<mat-form-field class="mat-block">
<mat-label translate>device.description</mat-label>
<textarea matInput formControlName="description" rows="2"></textarea>
</mat-form-field>
</fieldset>
</form>
</mat-step>
<mat-step [stepControl]="profileConfigFormGroup">
<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;">
<ng-template matStepLabel>{{ 'device-profile.transport-configuration' | translate }}</ng-template>
<tb-device-profile-transport-configuration
formControlName="transportConfiguration"
required>
</tb-device-profile-transport-configuration>
</form>
</mat-step>
<mat-step [stepControl]="alarmRulesFormGroup" *ngIf="createdProfile">
<form [formGroup]="alarmRulesFormGroup" style="padding-bottom: 16px;">
<ng-template matStepLabel>{{'device-profile.alarm-rules' | translate:
{count: alarmRulesFormGroup.get('alarms').value ?
alarmRulesFormGroup.get('alarms').value.length : 0} }}</ng-template>
<tb-device-profile-alarms
formControlName="alarms">
</tb-device-profile-alarms>
</form>
</mat-step>
<mat-step [stepControl]="specificConfigFormGroup">
<ng-template matStepLabel>{{ 'device.wizard.specific-configuration' | translate }}</ng-template>
<form [formGroup]="specificConfigFormGroup" style="padding-bottom: 16px;">
<tb-entity-autocomplete
formControlName="customerId"
labelText="device.wizard.customer-to-assign-device"
[entityType]="entityType.CUSTOMER">
</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>
</mat-step>
</mat-horizontal-stepper>
</div>
<div mat-dialog-actions fxLayout="row wrap" fxLayoutAlign="space-between center">
<button mat-button *ngIf="selectedIndex > 0"
[disabled]="(isLoading$ | async)"
(click)="previousStep()">{{ 'action.back' | translate }}</button>
<span *ngIf="selectedIndex == 0"></span>
<div fxLayout="row wrap" fxLayoutGap="20px">
<button mat-button
[disabled]="(isLoading$ | async)"
(click)="cancel()">{{ 'action.cancel' | translate }}</button>
<button mat-raised-button
[disabled]="(isLoading$ | async) || selectedForm.invalid"
color="primary"
(click)="nextStep()">{{ nextStepButtonLabel$ | async | translate }}</button>
</div>
</div>
</div>

38
ui-ngx/src/app/modules/home/components/wizard/device-wizard-dialog.component.scss

@ -0,0 +1,38 @@
/**
* 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.
*/
:host {
.mat-dialog-content {
display: flex;
flex-direction: column;
overflow: hidden;
.mat-stepper-horizontal {
display: flex;
flex-direction: column;
overflow: hidden;
}
}
}
:host ::ng-deep {
.mat-dialog-content {
.mat-stepper-horizontal {
.mat-horizontal-content-container {
overflow: auto;
}
}
}
}

286
ui-ngx/src/app/modules/home/components/wizard/device-wizard-dialog.component.ts

@ -0,0 +1,286 @@
///
/// 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, Inject, OnDestroy, SkipSelf, ViewChild } from '@angular/core';
import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog';
import { Store } from '@ngrx/store';
import { AppState } from '@core/core.state';
import { FormBuilder, FormControl, FormGroup, FormGroupDirective, NgForm, Validators } from '@angular/forms';
import { DialogComponent } from '@shared/components/dialog.component';
import { Router } from '@angular/router';
import {
createDeviceProfileConfiguration,
createDeviceProfileTransportConfiguration,
DeviceProfile,
DeviceProfileType,
DeviceTransportType,
deviceTransportTypeTranslationMap
} from '@shared/models/device.models';
import { MatHorizontalStepper } from '@angular/material/stepper';
import { AddEntityDialogData } from '@home/models/entity/entity-component.models';
import { BaseData, HasId } from '@shared/models/base-data';
import { EntityType } from '@shared/models/entity-type.models';
import { DeviceProfileService } from '@core/http/device-profile.service';
import { EntityId } from '@shared/models/id/entity-id';
import { BehaviorSubject, Observable, of, Subscription } from 'rxjs';
import { map, mergeMap, tap } from 'rxjs/operators';
import { DeviceService } from '@core/http/device.service';
import { ErrorStateMatcher } from '@angular/material/core';
import { StepperSelectionEvent } from '@angular/cdk/stepper';
@Component({
selector: 'tb-device-wizard',
templateUrl: './device-wizard-dialog.component.html',
providers: [],
styleUrls: ['./device-wizard-dialog.component.scss']
})
export class DeviceWizardDialogComponent extends
DialogComponent<DeviceWizardDialogComponent, boolean> implements OnDestroy, ErrorStateMatcher {
@ViewChild('addDeviceWizardStepper', {static: true}) addDeviceWizardStepper: MatHorizontalStepper;
selectedIndex = 0;
nextStepButtonLabel$ = new BehaviorSubject<string>('action.continue');
createdProfile = false;
entityType = EntityType;
deviceTransportTypes = Object.keys(DeviceTransportType);
deviceTransportTypeTranslations = deviceTransportTypeTranslationMap;
deviceWizardFormGroup: FormGroup;
profileConfigFormGroup: FormGroup;
transportConfigFormGroup: FormGroup;
alarmRulesFormGroup: FormGroup;
specificConfigFormGroup: FormGroup;
private subscriptions: Subscription[] = [];
constructor(protected store: Store<AppState>,
protected router: Router,
@Inject(MAT_DIALOG_DATA) public data: AddEntityDialogData<BaseData<EntityId>>,
@SkipSelf() private errorStateMatcher: ErrorStateMatcher,
public dialogRef: MatDialogRef<DeviceWizardDialogComponent, boolean>,
private deviceProfileService: DeviceProfileService,
private deviceService: DeviceService,
private fb: FormBuilder) {
super(store, router, dialogRef);
this.deviceWizardFormGroup = this.fb.group({
name: ['', Validators.required],
label: [''],
gateway: [false],
transportType: [DeviceTransportType.DEFAULT, Validators.required],
description: ['']
}
);
this.profileConfigFormGroup = this.fb.group({
addProfileType: [0],
deviceProfileId: [null, Validators.required],
newDeviceProfileTitle: [{value: null, disabled: true}]
}
);
this.subscriptions.push(this.profileConfigFormGroup.get('addProfileType').valueChanges.subscribe(
(addProfileType: number) => {
if (addProfileType === 0) {
this.profileConfigFormGroup.get('deviceProfileId').setValidators([Validators.required]);
this.profileConfigFormGroup.get('deviceProfileId').enable();
this.profileConfigFormGroup.get('newDeviceProfileTitle').setValidators(null);
this.profileConfigFormGroup.get('newDeviceProfileTitle').disable();
this.profileConfigFormGroup.updateValueAndValidity();
this.createdProfile = false;
} else {
this.profileConfigFormGroup.get('deviceProfileId').setValidators(null);
this.profileConfigFormGroup.get('deviceProfileId').disable();
this.profileConfigFormGroup.get('newDeviceProfileTitle').setValidators([Validators.required]);
this.profileConfigFormGroup.get('newDeviceProfileTitle').enable();
this.profileConfigFormGroup.updateValueAndValidity();
this.createdProfile = true;
}
}
));
this.transportConfigFormGroup = this.fb.group(
{
transportConfiguration: [createDeviceProfileTransportConfiguration(DeviceTransportType.DEFAULT), Validators.required]
}
);
this.subscriptions.push(this.deviceWizardFormGroup.get('transportType').valueChanges.subscribe((transportType) => {
this.deviceProfileTransportTypeChanged(transportType);
}));
this.alarmRulesFormGroup = this.fb.group({
alarms: [null]
}
);
this.specificConfigFormGroup = this.fb.group({
customerId: [null],
setCredential: [false],
credential: [{value: null, disabled: true}]
}
);
this.subscriptions.push(this.specificConfigFormGroup.get('setCredential').valueChanges.subscribe((value) => {
if (value) {
this.specificConfigFormGroup.get('credential').enable();
} else {
this.specificConfigFormGroup.get('credential').disable();
}
}));
}
ngOnDestroy() {
super.ngOnDestroy();
this.subscriptions.forEach(s => s.unsubscribe());
}
isErrorState(control: FormControl | null, form: FormGroupDirective | NgForm | null): boolean {
const originalErrorState = this.errorStateMatcher.isErrorState(control, form);
const customErrorState = !!(control && control.invalid);
return originalErrorState || customErrorState;
}
cancel(): void {
this.dialogRef.close(null);
}
previousStep(): void {
this.addDeviceWizardStepper.previous();
}
nextStep(): void {
if (this.selectedIndex < this.maxStepperIndex) {
this.addDeviceWizardStepper.next();
} else {
this.add();
}
}
get selectedForm(): FormGroup {
const index = !this.createdProfile && this.selectedIndex === this.maxStepperIndex ? 4 : this.selectedIndex;
switch (index) {
case 0:
return this.deviceWizardFormGroup;
case 1:
return this.profileConfigFormGroup;
case 2:
return this.transportConfigFormGroup;
case 3:
return this.alarmRulesFormGroup;
case 4:
return this.specificConfigFormGroup;
}
}
get maxStepperIndex(): number {
return this.addDeviceWizardStepper?._steps?.length - 1;
}
private deviceProfileTransportTypeChanged(deviceTransportType: DeviceTransportType): void {
this.transportConfigFormGroup.patchValue(
{transportConfiguration: createDeviceProfileTransportConfiguration(deviceTransportType)});
}
private add(): void {
this.creatProfile().pipe(
mergeMap(profileId => this.createdDevice(profileId)),
mergeMap(device => this.saveCredential(device))
).subscribe(
(created) => {
this.dialogRef.close(created);
}
);
}
private creatProfile(): Observable<EntityId> {
if (this.profileConfigFormGroup.get('addProfileType').value) {
const deviceProfile: DeviceProfile = {
name: this.profileConfigFormGroup.get('newDeviceProfileTitle').value,
type: DeviceProfileType.DEFAULT,
transportType: this.deviceWizardFormGroup.get('transportType').value,
profileData: {
configuration: createDeviceProfileConfiguration(DeviceProfileType.DEFAULT),
transportConfiguration: this.transportConfigFormGroup.get('transportConfiguration').value,
alarms: this.alarmRulesFormGroup.get('alarms').value
}
};
return this.deviceProfileService.saveDeviceProfile(deviceProfile).pipe(
map(profile => profile.id),
tap((profileId) => {
this.profileConfigFormGroup.patchValue({
deviceProfileId: profileId,
addProfileType: 0
});
this.addDeviceWizardStepper.selectedIndex = 2;
})
);
} else {
return of(null);
}
}
private createdDevice(profileId: EntityId = this.profileConfigFormGroup.get('deviceProfileId').value): Observable<BaseData<HasId>> {
const device = {
name: this.deviceWizardFormGroup.get('name').value,
label: this.deviceWizardFormGroup.get('label').value,
deviceProfileId: profileId,
additionalInfo: {
gateway: this.deviceWizardFormGroup.get('gateway').value,
description: this.deviceWizardFormGroup.get('description').value
},
customerId: null
};
if (this.specificConfigFormGroup.get('customerId').value) {
device.customerId = {
entityType: EntityType.CUSTOMER,
id: this.specificConfigFormGroup.get('customerId').value
};
}
return this.data.entitiesTableConfig.saveEntity(device);
}
private saveCredential(device: BaseData<HasId>): Observable<boolean> {
if (this.specificConfigFormGroup.get('setCredential').value) {
return this.deviceService.getDeviceCredentials(device.id.id).pipe(
mergeMap(
(deviceCredentials) => {
const deviceCredentialsValue = {...deviceCredentials, ...this.specificConfigFormGroup.value.credential};
return this.deviceService.saveDeviceCredentials(deviceCredentialsValue);
}
),
map(() => true));
}
return of(true);
}
changeStep($event: StepperSelectionEvent): void {
this.selectedIndex = $event.selectedIndex;
if (this.selectedIndex === this.maxStepperIndex) {
this.nextStepButtonLabel$.next('action.add');
} else {
this.nextStepButtonLabel$.next('action.continue');
}
}
}

3
ui-ngx/src/app/modules/home/pages/device-profile/device-profiles-table-config.resolver.ts

@ -114,7 +114,8 @@ export class DeviceProfilesTableConfigResolver implements Resolve<EntityTableCon
disableClose: true,
panelClass: ['tb-dialog', 'tb-fullscreen-dialog'],
data: {
deviceProfileName: null
deviceProfileName: null,
transportType: null
}
}).afterClosed();
}

36
ui-ngx/src/app/modules/home/pages/device/devices-table-config.resolver.ts

@ -29,7 +29,7 @@ import {
import { TranslateService } from '@ngx-translate/core';
import { DatePipe } from '@angular/common';
import { EntityType, entityTypeResources, entityTypeTranslations } from '@shared/models/entity-type.models';
import { EntityAction } from '@home/models/entity/entity-component.models';
import { AddEntityDialogData, EntityAction } from '@home/models/entity/entity-component.models';
import { Device, DeviceCredentials, DeviceInfo } from '@app/shared/models/device.models';
import { DeviceComponent } from '@modules/home/pages/device/device.component';
import { forkJoin, Observable, of } from 'rxjs';
@ -61,6 +61,8 @@ import {
} from '../../dialogs/add-entities-to-customer-dialog.component';
import { DeviceTabsComponent } from '@home/pages/device/device-tabs.component';
import { HomeDialogsService } from '@home/dialogs/home-dialogs.service';
import { DeviceWizardDialogComponent } from '@home/components/wizard/device-wizard-dialog.component';
import { BaseData, HasId } from '@shared/models/base-data';
@Injectable()
export class DevicesTableConfigResolver implements Resolve<EntityTableConfig<DeviceInfo>> {
@ -221,7 +223,7 @@ export class DevicesTableConfigResolver implements Resolve<EntityTableConfig<Dev
{
name: this.translate.instant('device.manage-credentials'),
icon: 'security',
isEnabled: (entity) => true,
isEnabled: () => true,
onAction: ($event, entity) => this.manageCredentials($event, entity)
}
);
@ -243,7 +245,7 @@ export class DevicesTableConfigResolver implements Resolve<EntityTableConfig<Dev
{
name: this.translate.instant('device.manage-credentials'),
icon: 'security',
isEnabled: (entity) => true,
isEnabled: () => true,
onAction: ($event, entity) => this.manageCredentials($event, entity)
}
);
@ -253,7 +255,7 @@ export class DevicesTableConfigResolver implements Resolve<EntityTableConfig<Dev
{
name: this.translate.instant('device.view-credentials'),
icon: 'security',
isEnabled: (entity) => true,
isEnabled: () => true,
onAction: ($event, entity) => this.manageCredentials($event, entity)
}
);
@ -301,7 +303,13 @@ export class DevicesTableConfigResolver implements Resolve<EntityTableConfig<Dev
icon: 'file_upload',
isEnabled: () => true,
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') {
@ -326,6 +334,23 @@ export class DevicesTableConfigResolver implements Resolve<EntityTableConfig<Dev
});
}
deviceWizard($event: Event) {
this.dialog.open<DeviceWizardDialogComponent, AddEntityDialogData<BaseData<HasId>>,
boolean>(DeviceWizardDialogComponent, {
disableClose: true,
panelClass: ['tb-dialog', 'tb-fullscreen-dialog'],
data: {
entitiesTableConfig: this.config.table.entitiesTableConfig
}
}).afterClosed().subscribe(
(res) => {
if (res) {
this.config.table.updateData();
}
}
);
}
addDevicesToCustomer($event: Event) {
if ($event) {
$event.stopPropagation();
@ -480,5 +505,4 @@ export class DevicesTableConfigResolver implements Resolve<EntityTableConfig<Dev
}
return false;
}
}

12
ui-ngx/src/assets/locale/locale.constant-en_US.json

@ -756,7 +756,17 @@
"search": "Search devices",
"selected-devices": "{ count, plural, 1 {1 device} other {# devices} } selected",
"device-configuration": "Device configuration",
"transport-configuration": "Transport configuration"
"transport-configuration": "Transport configuration",
"wizard": {
"device-wizard": "Device Wizard",
"device-details": "Device details",
"profile-configuration": "Profile configuration",
"new-device-profile": "New device profile",
"existing-device-profile": "Select existing device profile",
"specific-configuration": "Specific configuration",
"customer-to-assign-device": "Customer to assign the device",
"add-credential": "Add credential"
}
},
"device-profile": {
"device-profile": "Device profile",

Loading…
Cancel
Save