Browse Source

Merge branch 'feature/lts-4.3-iot-hub' of github.com:thingsboard/thingsboard into feature/lts-4.3-iot-hub

pull/15540/head
Igor Kulikov 4 months ago
parent
commit
d45ff1b239
  1. 81
      ui-ngx/src/app/modules/home/components/iot-hub/iot-hub-install-dialog.component.html
  2. 95
      ui-ngx/src/app/modules/home/components/iot-hub/iot-hub-install-dialog.component.ts
  3. 6
      ui-ngx/src/assets/locale/locale.constant-en_US.json

81
ui-ngx/src/app/modules/home/components/iot-hub/iot-hub-install-dialog.component.html

@ -19,26 +19,55 @@
@switch (state) { @switch (state) {
@case ('confirm') { @case ('confirm') {
<h2 class="tb-iot-hub-install-title">{{ 'iot-hub.install-confirm-title' | translate:{ name: item.name } }}</h2> <h2 class="tb-iot-hub-install-title">{{ 'iot-hub.install-confirm-title' | translate:{ name: item.name } }}</h2>
@if (item.type === ItemType.RULE_CHAIN) { <p class="tb-iot-hub-install-desc">{{ 'iot-hub.install-desc' | translate }}</p>
<p class="tb-iot-hub-install-desc">{{ 'iot-hub.rule-chain-install-desc' | translate }}</p>
} @else {
<p class="tb-iot-hub-install-desc">{{ 'iot-hub.install-desc' | translate }}</p>
}
} }
@case ('select-entity') { @case ('select-entity') {
<h2 class="tb-iot-hub-install-title">{{ 'iot-hub.install-confirm-title' | translate:{ name: item.name } }}</h2> <h2 class="tb-iot-hub-install-title">{{ 'iot-hub.install-confirm-title' | translate:{ name: item.name } }}</h2>
@if (activeSelectEntityConfig?.promptKey) { @if (item.type === ItemType.RULE_CHAIN) {
<p class="tb-iot-hub-install-desc" <p class="tb-iot-hub-install-desc">{{ 'iot-hub.rule-chain-install-subtitle' | translate }}</p>
[innerHTML]="activeSelectEntityConfig.promptKey | translate:{ name: item.name }"></p> <form [formGroup]="ruleChainInstallForm">
<div class="tb-form-panel stroked">
<mat-slide-toggle class="mat-slide flex" formControlName="setAsDefault">
<div tb-hint-tooltip-icon="{{ 'iot-hub.rule-chain-set-as-profile-default-tooltip' | translate }}">
{{ 'iot-hub.rule-chain-set-as-profile-default-toggle' | translate }}
</div>
</mat-slide-toggle>
@if (ruleChainInstallForm.controls.setAsDefault.value) {
<div class="flex flex-row gap-4 xs:flex-col xs:gap-0">
<tb-entity-type-select
class="flex-1"
appearance="outline"
[showLabel]="true"
[required]="true"
[allowedEntityTypes]="[EntityType.DEVICE_PROFILE, EntityType.ASSET_PROFILE]"
[filterAllowedEntityTypes]="false"
formControlName="entityType">
</tb-entity-type-select>
<tb-entity-autocomplete
class="flex-1"
appearance="outline"
[required]="true"
[entityType]="ruleChainInstallForm.controls.entityType.value"
formControlName="entityId">
</tb-entity-autocomplete>
</div>
}
</div>
</form>
} @else {
@if (activeSelectEntityConfig?.promptKey) {
<p class="tb-iot-hub-install-desc"
[innerHTML]="activeSelectEntityConfig.promptKey | translate:{ name: item.name }"></p>
}
<tb-entity-select
[(ngModel)]="selectedEntityId"
[allowedEntityTypes]="activeSelectEntityConfig.allowed"
[defaultEntityType]="activeSelectEntityConfig.defaultType"
[filterAllowedEntityTypes]="false"
appearance="outline"
[required]="activeSelectEntityConfig.required">
</tb-entity-select>
} }
<tb-entity-select
[(ngModel)]="selectedEntityId"
[allowedEntityTypes]="activeSelectEntityConfig.allowed"
[defaultEntityType]="activeSelectEntityConfig.defaultType"
[filterAllowedEntityTypes]="false"
appearance="outline"
[required]="activeSelectEntityConfig.required">
</tb-entity-select>
} }
@case ('confirm-overwrite') { @case ('confirm-overwrite') {
<h2 class="tb-iot-hub-install-title">{{ 'iot-hub.rule-chain-overwrite-title' | translate }}</h2> <h2 class="tb-iot-hub-install-title">{{ 'iot-hub.rule-chain-overwrite-title' | translate }}</h2>
@ -59,7 +88,7 @@
@case ('error') { @case ('error') {
<h2 class="tb-iot-hub-install-title">{{ 'iot-hub.install-error-title' | translate }}</h2> <h2 class="tb-iot-hub-install-title">{{ 'iot-hub.install-error-title' | translate }}</h2>
<p class="tb-iot-hub-install-desc">{{ 'iot-hub.install-error-message' | translate:{ name: item.name } }}</p> <p class="tb-iot-hub-install-desc">{{ 'iot-hub.install-error-message' | translate:{ name: item.name } }}</p>
@if (item?.type === ItemType.SOLUTION_TEMPLATE) { @if (item.type === ItemType.SOLUTION_TEMPLATE) {
<tb-iot-hub-markdown class="tb-iot-hub-error-details-markdown" [data]="errorMessage"></tb-iot-hub-markdown> <tb-iot-hub-markdown class="tb-iot-hub-error-details-markdown" [data]="errorMessage"></tb-iot-hub-markdown>
} @else { } @else {
<div class="tb-iot-hub-error-details"> <div class="tb-iot-hub-error-details">
@ -73,28 +102,20 @@
@switch (state) { @switch (state) {
@case ('confirm') { @case ('confirm') {
<button mat-button (click)="cancel()">{{ 'action.cancel' | translate }}</button> <button mat-button (click)="cancel()">{{ 'action.cancel' | translate }}</button>
@if (item.type === ItemType.RULE_CHAIN) { <button mat-flat-button color="primary" (click)="install()">{{ 'iot-hub.install' | translate }}</button>
<button mat-stroked-button (click)="install()">{{ 'iot-hub.install' | translate }}</button>
<button mat-flat-button color="primary" (click)="installAsEntityProfileDefault()">
{{ 'iot-hub.rule-chain-install-as-default' | translate }}
</button>
} @else {
<button mat-flat-button color="primary" (click)="install()">{{ 'iot-hub.install' | translate }}</button>
}
} }
@case ('select-entity') { @case ('select-entity') {
<button mat-button (click)="cancel()">{{ 'action.cancel' | translate }}</button> <button mat-button (click)="cancel()">{{ 'action.cancel' | translate }}</button>
@if (item.type === ItemType.RULE_CHAIN) { @if (item.type === ItemType.RULE_CHAIN) {
<button mat-button (click)="selectEntityBack()">{{ 'action.back' | translate }}</button>
<button mat-flat-button color="primary" <button mat-flat-button color="primary"
[disabled]="!selectedEntityId" [disabled]="ruleChainInstallForm.invalid"
(click)="onSelectEntityInstall()"> (click)="onRuleChainInstall()">
{{ 'iot-hub.install' | translate }} {{ 'iot-hub.install' | translate }}
</button> </button>
} @else { } @else {
<button mat-flat-button color="primary" <button mat-flat-button color="primary"
[disabled]="activeSelectEntityConfig?.required && !selectedEntityId" [disabled]="activeSelectEntityConfig.required && !selectedEntityId"
(click)="onSelectEntityInstall()"> (click)="onEntitySelectInstall()">
{{ 'iot-hub.install' | translate }} {{ 'iot-hub.install' | translate }}
</button> </button>
} }

95
ui-ngx/src/app/modules/home/components/iot-hub/iot-hub-install-dialog.component.ts

@ -14,9 +14,11 @@
/// limitations under the License. /// limitations under the License.
/// ///
import { Component, Inject } from '@angular/core'; import { Component, DestroyRef, Inject } from '@angular/core';
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
import { MAT_DIALOG_DATA, MatDialog, MatDialogRef } from '@angular/material/dialog'; import { MAT_DIALOG_DATA, MatDialog, MatDialogRef } from '@angular/material/dialog';
import { Router } from '@angular/router'; import { Router } from '@angular/router';
import { FormBuilder, FormControl, FormGroup, Validators } from '@angular/forms';
import { Store } from '@ngrx/store'; import { Store } from '@ngrx/store';
import { AppState } from '@core/core.state'; import { AppState } from '@core/core.state';
import { DialogComponent } from '@shared/components/dialog.component'; import { DialogComponent } from '@shared/components/dialog.component';
@ -70,15 +72,21 @@ export type InstallState =
export class TbIotHubInstallDialogComponent extends DialogComponent<TbIotHubInstallDialogComponent> { export class TbIotHubInstallDialogComponent extends DialogComponent<TbIotHubInstallDialogComponent> {
ItemType = ItemType; ItemType = ItemType;
EntityType = EntityType;
item: MpItemVersionView; item: MpItemVersionView;
typeTranslations = itemTypeTranslations; typeTranslations = itemTypeTranslations;
state: InstallState = 'confirm'; state!: InstallState;
errorMessage = ''; errorMessage = '';
entityDetailsUrl: string | null = null; entityDetailsUrl: string | null = null;
selectedEntityId: EntityId | null = null; selectedEntityId: EntityId | null = null;
pendingOverwrite: PendingOverwrite | null = null; pendingOverwrite: PendingOverwrite | null = null;
ruleChainInstallForm!: FormGroup<{
setAsDefault: FormControl<boolean>;
entityType: FormControl<EntityType>;
entityId: FormControl<string | null>;
}>;
private readonly selectEntityConfig: Partial<Record<ItemType, SelectEntityConfig>> = { private readonly selectEntityConfig: Partial<Record<ItemType, SelectEntityConfig>> = {
[ItemType.CALCULATED_FIELD]: { [ItemType.CALCULATED_FIELD]: {
@ -92,13 +100,7 @@ export class TbIotHubInstallDialogComponent extends DialogComponent<TbIotHubInst
defaultType: EntityType.DEVICE_PROFILE, defaultType: EntityType.DEVICE_PROFILE,
required: true, required: true,
promptKey: 'iot-hub.select-entity-for-alarm-rule', promptKey: 'iot-hub.select-entity-for-alarm-rule',
}, }
[ItemType.RULE_CHAIN]: {
allowed: [EntityType.DEVICE_PROFILE, EntityType.ASSET_PROFILE],
defaultType: EntityType.DEVICE_PROFILE,
required: true,
promptKey: 'iot-hub.select-profile-for-rule-chain',
},
}; };
get activeSelectEntityConfig(): SelectEntityConfig | null { get activeSelectEntityConfig(): SelectEntityConfig | null {
@ -115,10 +117,47 @@ export class TbIotHubInstallDialogComponent extends DialogComponent<TbIotHubInst
private iotHubApiService: IotHubApiService, private iotHubApiService: IotHubApiService,
private deviceProfileService: DeviceProfileService, private deviceProfileService: DeviceProfileService,
private assetProfileService: AssetProfileService, private assetProfileService: AssetProfileService,
private ruleChainService: RuleChainService private ruleChainService: RuleChainService,
private fb: FormBuilder,
private destroyRef: DestroyRef
) { ) {
super(store, router, dialogRef); super(store, router, dialogRef);
this.item = data.item; this.item = data.item;
this.state = this.computeInitialState();
if (this.item.type === ItemType.RULE_CHAIN) {
this.initRuleChainForm();
}
}
private computeInitialState(): InstallState {
return this.activeSelectEntityConfig || this.item.type === ItemType.RULE_CHAIN
? 'select-entity'
: 'confirm';
}
private initRuleChainForm(): void {
this.ruleChainInstallForm = this.fb.group({
setAsDefault: this.fb.nonNullable.control<boolean>(false),
entityType: this.fb.nonNullable.control<EntityType>(EntityType.DEVICE_PROFILE),
entityId: this.fb.control<string | null>(null)
});
this.ruleChainInstallForm.controls.setAsDefault.valueChanges
.pipe(takeUntilDestroyed(this.destroyRef))
.subscribe(setAsDefault => {
const entityIdCtrl = this.ruleChainInstallForm.controls.entityId;
if (setAsDefault) {
entityIdCtrl.setValidators(Validators.required);
} else {
entityIdCtrl.clearValidators();
entityIdCtrl.setValue(null);
}
entityIdCtrl.updateValueAndValidity();
});
this.ruleChainInstallForm.controls.entityType.valueChanges
.pipe(takeUntilDestroyed(this.destroyRef))
.subscribe(() => {
this.ruleChainInstallForm.controls.entityId.setValue(null);
});
} }
getTypeLabel(): string { getTypeLabel(): string {
@ -126,39 +165,33 @@ export class TbIotHubInstallDialogComponent extends DialogComponent<TbIotHubInst
return key ? this.translate.instant(key) : ''; return key ? this.translate.instant(key) : '';
} }
install(): void { onEntitySelectInstall(): void {
if (this.item.type === ItemType.CALCULATED_FIELD || this.item.type === ItemType.ALARM_RULE) { if (!this.selectedEntityId) {
this.state = 'select-entity';
return; return;
} }
this.doInstall(); this.install();
} }
installAsEntityProfileDefault(): void { onRuleChainInstall(): void {
this.state = 'select-entity'; const { setAsDefault, entityType, entityId } = this.ruleChainInstallForm.getRawValue();
} if (!setAsDefault) {
this.selectedEntityId = null;
selectEntityBack(): void { this.install();
this.selectedEntityId = null;
this.state = 'confirm';
}
onSelectEntityInstall(): void {
if (!this.selectedEntityId) {
return; return;
} }
if (this.item.type !== ItemType.RULE_CHAIN) { if (!entityId) {
this.doInstall();
return; return;
} }
this.resolveOverwrite(this.selectedEntityId).subscribe({ const profileEntityId: EntityId = { entityType, id: entityId };
this.selectedEntityId = profileEntityId;
this.resolveOverwrite(profileEntityId).subscribe({
next: (pending) => { next: (pending) => {
if (pending) { if (pending) {
this.pendingOverwrite = pending; this.pendingOverwrite = pending;
this.state = 'confirm-overwrite'; this.state = 'confirm-overwrite';
} else { } else {
this.pendingOverwrite = null; this.pendingOverwrite = null;
this.doInstall(); this.install();
} }
}, },
error: (err) => { error: (err) => {
@ -170,7 +203,7 @@ export class TbIotHubInstallDialogComponent extends DialogComponent<TbIotHubInst
} }
confirmOverwriteReplace(): void { confirmOverwriteReplace(): void {
this.doInstall(); this.install();
} }
confirmOverwriteCancel(): void { confirmOverwriteCancel(): void {
@ -204,7 +237,7 @@ export class TbIotHubInstallDialogComponent extends DialogComponent<TbIotHubInst
); );
} }
doInstall(): void { install(): void {
this.state = 'installing'; this.state = 'installing';
const versionId = this.item.id as string; const versionId = this.item.id as string;
const data = this.selectedEntityId ? { entityId: this.selectedEntityId } : undefined; const data = this.selectedEntityId ? { entityId: this.selectedEntityId } : undefined;

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

@ -4307,9 +4307,9 @@
"remove-anyway": "Remove anyway", "remove-anyway": "Remove anyway",
"select-entity-for-cf": "Select entity for calculated field '{{name}}'", "select-entity-for-cf": "Select entity for calculated field '{{name}}'",
"select-entity-for-alarm-rule": "Select entity for alarm rule '{{name}}'", "select-entity-for-alarm-rule": "Select entity for alarm rule '{{name}}'",
"rule-chain-install-desc": "Installing this will automatically create the rule chain within your tenant. Use 'Set for profile' to also set it as default for Device or Asset profile.", "rule-chain-install-subtitle": "Installing automatically creates the rule chain within your tenant. Optionally set it as a profile's default so entities of that profile route through it.",
"rule-chain-install-as-default": "Set for profile", "rule-chain-set-as-profile-default-toggle": "Set as profile default rule chain",
"select-profile-for-rule-chain": "Select profile for rule chain '{{name}}'", "rule-chain-set-as-profile-default-tooltip": "If enabled, all entities of the selected profile will route messages through this rule chain.",
"rule-chain-overwrite-title": "Replace existing default rule chain?", "rule-chain-overwrite-title": "Replace existing default rule chain?",
"rule-chain-overwrite-body": "Profile <b>{{profile}}</b> currently uses <b>{{existing}}</b> as its default rule chain. Installing will replace it with this rule chain.", "rule-chain-overwrite-body": "Profile <b>{{profile}}</b> currently uses <b>{{existing}}</b> as its default rule chain. Installing will replace it with this rule chain.",
"rule-chain-overwrite-replace": "Replace", "rule-chain-overwrite-replace": "Replace",

Loading…
Cancel
Save