Browse Source

Add IoT Hub integration for calculated fields, subtype support, error handling

- IotHubApiService injected in dialog constructor, removed from data interface
- Add optional entityId to dialog data, passed to installItemVersion for CF
- Add optional itemSubType to dialog data and fixedSubType to browse component
- fixedSubType: applies subtype filter, hides subtype filter section/chips,
  excluded from active filters and clear all, hides card subtype badge
- Add "Add from IoT Hub" to calculated fields table with entityId
- Rule chains: pass CORE/EDGE subType based on ruleChainScope, fix navigation
- Show alert dialog on install failure with error details
- Remove IotHubApiService from all dialog data callers
pull/15508/head
Igor Kulikov 6 months ago
parent
commit
c1b508299d
  1. 29
      ui-ngx/src/app/modules/home/components/calculated-fields/calculated-fields-table-config.ts
  2. 1
      ui-ngx/src/app/modules/home/components/iot-hub/iot-hub-add-item-dialog.component.html
  3. 27
      ui-ngx/src/app/modules/home/components/iot-hub/iot-hub-add-item-dialog.component.ts
  4. 8
      ui-ngx/src/app/modules/home/components/iot-hub/iot-hub-browse.component.html
  5. 17
      ui-ngx/src/app/modules/home/components/iot-hub/iot-hub-browse.component.ts
  6. 5
      ui-ngx/src/app/modules/home/pages/dashboard/dashboards-table-config.resolver.ts
  7. 13
      ui-ngx/src/app/modules/home/pages/rulechain/rulechains-table-config.resolver.ts

29
ui-ngx/src/app/modules/home/components/calculated-fields/calculated-fields-table-config.ts

@ -57,6 +57,12 @@ import { ImportExportService } from '@shared/import-export/import-export.service
import { isObject } from '@core/utils';
import { EntityDebugSettingsService } from '@home/components/entity/debug/entity-debug-settings.service';
import { DatePipe } from '@angular/common';
import { ItemType } from '@shared/models/iot-hub/iot-hub-item.models';
import {
TbIotHubAddItemDialogComponent,
IotHubAddItemDialogData,
IotHubAddItemDialogResult
} from '@home/components/iot-hub/iot-hub-add-item-dialog.component';
export class CalculatedFieldsTableConfig extends EntityTableConfig<CalculatedField> {
@ -104,6 +110,12 @@ export class CalculatedFieldsTableConfig extends EntityTableConfig<CalculatedFie
icon: 'file_upload',
isEnabled: () => true,
onAction: () => this.importCalculatedField()
},
{
name: this.translate.instant('iot-hub.add-from-iot-hub'),
icon: 'store',
isEnabled: () => true,
onAction: () => this.addCalculatedFieldFromIotHub()
}
];
@ -243,6 +255,23 @@ export class CalculatedFieldsTableConfig extends EntityTableConfig<CalculatedFie
this.importExportService.exportCalculatedField(calculatedField.id.id);
}
private addCalculatedFieldFromIotHub(): void {
const dialogRef = this.dialog.open(TbIotHubAddItemDialogComponent, {
panelClass: ['tb-dialog', 'tb-fullscreen-dialog'],
disableClose: true,
autoFocus: false,
data: {
itemType: ItemType.CALCULATED_FIELD,
entityId: this.entityId
} as IotHubAddItemDialogData
});
dialogRef.afterClosed().subscribe((result: IotHubAddItemDialogResult) => {
if (result?.descriptor) {
this.updateData();
}
});
}
private importCalculatedField(): void {
this.importExportService.openCalculatedFieldImportDialog()
.pipe(

1
ui-ngx/src/app/modules/home/components/iot-hub/iot-hub-add-item-dialog.component.html

@ -33,6 +33,7 @@
}
<tb-iot-hub-browse
[activeType]="itemType"
[fixedSubType]="itemSubType"
[hideTabs]="true"
[embedded]="true"
mode="add"

27
ui-ngx/src/app/modules/home/components/iot-hub/iot-hub-add-item-dialog.component.ts

@ -19,11 +19,14 @@ import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog';
import { MpItemVersionView } from '@shared/models/iot-hub/iot-hub-version.models';
import { ItemType, itemTypeTranslations } from '@shared/models/iot-hub/iot-hub-item.models';
import { IotHubApiService } from '@core/http/iot-hub-api.service';
import { DialogService } from '@core/services/dialog.service';
import { TranslateService } from '@ngx-translate/core';
import { EntityId } from '@shared/models/id/entity-id';
export interface IotHubAddItemDialogData {
itemType: ItemType;
iotHubApiService: IotHubApiService;
itemSubType?: string;
entityId?: EntityId;
}
export interface IotHubAddItemDialogResult {
@ -40,14 +43,18 @@ export interface IotHubAddItemDialogResult {
export class TbIotHubAddItemDialogComponent {
itemType: ItemType;
itemSubType: string;
isInstalling = false;
constructor(
@Inject(MAT_DIALOG_DATA) public data: IotHubAddItemDialogData,
private dialogRef: MatDialogRef<TbIotHubAddItemDialogComponent>,
private translate: TranslateService
private translate: TranslateService,
private iotHubApiService: IotHubApiService,
private dialogService: DialogService
) {
this.itemType = data.itemType;
this.itemSubType = data.itemSubType;
}
getTitle(): string {
@ -59,15 +66,27 @@ export class TbIotHubAddItemDialogComponent {
onAddItem(item: MpItemVersionView): void {
this.isInstalling = true;
const versionId = item.id as string;
this.data.iotHubApiService.installItemVersion(versionId, { ignoreLoading: true }).subscribe({
const installData = this.data.entityId ? { entityId: this.data.entityId } : undefined;
this.iotHubApiService.installItemVersion(versionId, { ignoreLoading: true }, installData).subscribe({
next: (result) => {
this.isInstalling = false;
if (result.success) {
this.dialogRef.close({ item, descriptor: result.descriptor } as IotHubAddItemDialogResult);
} else {
const message = result.errorMessage || this.translate.instant('iot-hub.install-error', { name: item.name });
this.dialogService.alert(
this.translate.instant('iot-hub.install-error-title'),
message
);
}
},
error: () => {
error: (err) => {
this.isInstalling = false;
const message = err?.error?.message || err?.message || this.translate.instant('iot-hub.install-error', { name: item.name });
this.dialogService.alert(
this.translate.instant('iot-hub.install-error-title'),
message
);
}
});
}

8
ui-ngx/src/app/modules/home/components/iot-hub/iot-hub-browse.component.html

@ -57,7 +57,7 @@
<div class="tb-iot-hub-filters">
<div class="tb-iot-hub-filters-wrapper">
<div class="tb-iot-hub-filters-inner">
@if (getSubtypeMap(); as subtypeMap) {
@if (!fixedSubType && getSubtypeMap(); as subtypeMap) {
<mat-expansion-panel [expanded]="true" class="tb-iot-hub-filter-panel">
<mat-expansion-panel-header>
<mat-panel-title>{{ 'iot-hub.type-filter-label' | translate }}</mat-panel-title>
@ -72,7 +72,7 @@
}
@if (categories.size > 0) {
@if (getSubtypeMap()) {
@if (!fixedSubType && getSubtypeMap()) {
<div class="tb-iot-hub-filter-divider"></div>
}
<mat-expansion-panel [expanded]="true" class="tb-iot-hub-filter-panel">
@ -88,7 +88,7 @@
</mat-expansion-panel>
}
@if (getSubtypeMap() || categories.size > 0) {
@if ((!fixedSubType && getSubtypeMap()) || categories.size > 0) {
<div class="tb-iot-hub-filter-divider"></div>
}
<mat-expansion-panel [expanded]="false" class="tb-iot-hub-filter-panel">
@ -208,7 +208,7 @@
[item]="item"
[installedItem]="getInstalledItem(item)"
[showTypeChip]="false"
[showSubtype]="true"
[showSubtype]="!fixedSubType"
[showCreator]="!creatorId"
[mode]="mode"
(cardClick)="openItemDetail($event)"

17
ui-ngx/src/app/modules/home/components/iot-hub/iot-hub-browse.component.ts

@ -67,6 +67,7 @@ export class TbIotHubBrowseComponent implements OnInit, OnDestroy {
@Input() embedded = false;
@Input() hideTabs = false;
@Input() mode: 'default' | 'add' = 'default';
@Input() fixedSubType: string;
@Output() addItem = new EventEmitter<MpItemVersionView>();
@Input() set activeType(value: ItemType) {
if (value && value !== this._activeType) {
@ -136,6 +137,12 @@ export class TbIotHubBrowseComponent implements OnInit, OnDestroy {
if (params['search']) {
this.textSearch = params['search'];
}
if (this.fixedSubType) {
const subtypes = this.getActiveSubtypes();
if (subtypes) {
subtypes.add(this.fixedSubType);
}
}
this.updateCategories();
if (this.activeType === ItemType.WIDGET) {
this.loadInstalledWidgets();
@ -304,6 +311,9 @@ export class TbIotHubBrowseComponent implements OnInit, OnDestroy {
}
getActiveSubtypesArray(): string[] {
if (this.fixedSubType) {
return Array.from(this.getActiveSubtypes()).filter(s => s !== this.fixedSubType);
}
return Array.from(this.getActiveSubtypes());
}
@ -359,6 +369,9 @@ export class TbIotHubBrowseComponent implements OnInit, OnDestroy {
this.activeCfTypes.clear();
this.activeWidgetTypes.clear();
this.activeRuleChainTypes.clear();
if (this.fixedSubType) {
this.getActiveSubtypes()?.add(this.fixedSubType);
}
this.textSearch = '';
this.updateCategories();
this.pageIndex = 0;
@ -366,9 +379,9 @@ export class TbIotHubBrowseComponent implements OnInit, OnDestroy {
}
hasActiveDropdownFilters(): boolean {
const subtypeCount = this.fixedSubType ? this.getActiveSubtypesArray().length : (this.getActiveSubtypes()?.size || 0);
return this.activeCategories.size > 0 ||
this.activeUseCases.size > 0 || this.activeCfTypes.size > 0 ||
this.activeWidgetTypes.size > 0 || this.activeRuleChainTypes.size > 0;
this.activeUseCases.size > 0 || subtypeCount > 0;
}
hasActiveFilters(): boolean {

5
ui-ngx/src/app/modules/home/pages/dashboard/dashboards-table-config.resolver.ts

@ -78,7 +78,6 @@ import {
EntityAliasesDialogData
} from '@home/components/alias/entity-aliases-dialog.component';
import { ItemType } from '@shared/models/iot-hub/iot-hub-item.models';
import { IotHubApiService } from '@core/http/iot-hub-api.service';
import {
TbIotHubAddItemDialogComponent,
IotHubAddItemDialogData,
@ -97,7 +96,6 @@ export class DashboardsTableConfigResolver {
private dialogService: DialogService,
private homeDialogs: HomeDialogsService,
private importExport: ImportExportService,
private iotHubApiService: IotHubApiService,
private translate: TranslateService,
private datePipe: DatePipe,
private router: Router,
@ -408,8 +406,7 @@ export class DashboardsTableConfigResolver {
disableClose: true,
autoFocus: false,
data: {
itemType: ItemType.DASHBOARD,
iotHubApiService: this.iotHubApiService
itemType: ItemType.DASHBOARD
} as IotHubAddItemDialogData
});
dialogRef.afterClosed().subscribe((result: IotHubAddItemDialogResult) => {

13
ui-ngx/src/app/modules/home/pages/rulechain/rulechains-table-config.resolver.ts

@ -50,7 +50,6 @@ import { PageLink } from '@shared/models/page/page-link';
import { Edge } from '@shared/models/edge.models';
import { mergeMap } from 'rxjs/operators';
import { ItemType } from '@shared/models/iot-hub/iot-hub-item.models';
import { IotHubApiService } from '@core/http/iot-hub-api.service';
import {
TbIotHubAddItemDialogComponent,
IotHubAddItemDialogData,
@ -70,7 +69,6 @@ export class RuleChainsTableConfigResolver {
private importExport: ImportExportService,
private itembuffer: ItemBufferService,
private edgeService: EdgeService,
private iotHubApiService: IotHubApiService,
private translate: TranslateService,
private datePipe: DatePipe,
private router: Router,
@ -303,18 +301,25 @@ export class RuleChainsTableConfigResolver {
}
addRuleChainFromIotHub() {
const ruleChainScope = this.config.componentsData.ruleChainScope;
const ruleChainType = ruleChainScope === 'edges' ? 'EDGE' : 'CORE';
const dialogRef = this.dialog.open(TbIotHubAddItemDialogComponent, {
panelClass: ['tb-dialog', 'tb-fullscreen-dialog'],
disableClose: true,
autoFocus: false,
data: {
itemType: ItemType.RULE_CHAIN,
iotHubApiService: this.iotHubApiService
itemSubType: ruleChainType
} as IotHubAddItemDialogData
});
dialogRef.afterClosed().subscribe((result: IotHubAddItemDialogResult) => {
if (result?.descriptor?.type === 'RULE_CHAIN' && result.descriptor.ruleChainId?.id) {
this.router.navigateByUrl(`ruleChains/${result.descriptor.ruleChainId.id}`);
const ruleChainId = result.descriptor.ruleChainId.id;
if (ruleChainScope === 'edges') {
this.router.navigateByUrl(`edgeManagement/ruleChains/${ruleChainId}`);
} else {
this.router.navigateByUrl(`ruleChains/${ruleChainId}`);
}
}
});
}

Loading…
Cancel
Save