Browse Source

fix(iot-hub): tighten delete/update result handling and switch button spinners/icons to matButtonIcon

IotHubActionsService.deleteItem now returns true/false (no cancel-as-EMPTY) and updateItem returns string|boolean; callers no longer early-return on a null installed item and instead let the service produce a false, so the subscriber consistently fires. Installed-items table shows a translucent loading overlay during update/delete via isLoading + relative-positioned container. Replace mr-2 inline-block align-middle hacks on dialog button spinners with matButtonIcon, and use matButtonIcon for the check-for-updates button icon/spinner too.
pull/15842/head
Igor Kulikov 2 months ago
parent
commit
f5a1052a48
  1. 21
      ui-ngx/src/app/modules/home/components/iot-hub/iot-hub-actions.service.ts
  2. 10
      ui-ngx/src/app/modules/home/components/iot-hub/iot-hub-browse.component.ts
  3. 10
      ui-ngx/src/app/modules/home/components/iot-hub/iot-hub-install-dialog.component.html
  4. 17
      ui-ngx/src/app/modules/home/components/iot-hub/iot-hub-installed-items-table.component.html
  5. 1
      ui-ngx/src/app/modules/home/components/iot-hub/iot-hub-installed-items-table.component.scss
  6. 10
      ui-ngx/src/app/modules/home/components/iot-hub/iot-hub-installed-items-table.component.ts
  7. 4
      ui-ngx/src/app/modules/home/components/iot-hub/iot-hub-item-detail-dialog.component.ts
  8. 8
      ui-ngx/src/app/modules/home/components/iot-hub/iot-hub-search.component.ts
  9. 2
      ui-ngx/src/app/modules/home/components/iot-hub/iot-hub-update-dialog.component.html
  10. 4
      ui-ngx/src/app/modules/home/components/iot-hub/iot-hub-update-dialog.component.ts
  11. 31
      ui-ngx/src/app/modules/home/pages/iot-hub/iot-hub-home.component.ts
  12. 4
      ui-ngx/src/app/modules/home/pages/iot-hub/iot-hub-installed-items.component.html

21
ui-ngx/src/app/modules/home/components/iot-hub/iot-hub-actions.service.ts

@ -17,7 +17,7 @@
import { Injectable } from '@angular/core';
import { MatDialog } from '@angular/material/dialog';
import { Observable, of, EMPTY } from 'rxjs';
import { filter, mergeMap } from 'rxjs/operators';
import { filter, map, mergeMap } from 'rxjs/operators';
import { TranslateService } from '@ngx-translate/core';
import { DialogService } from '@core/services/dialog.service';
import { MpItemVersionView } from '@shared/models/iot-hub/iot-hub-version.models';
@ -94,11 +94,11 @@ export class IotHubActionsService {
}).afterClosed();
}
updateItem(installedItem: IotHubInstalledItem, version: string, versionId: string): Observable<string> {
updateItem(installedItem: IotHubInstalledItem, version: string, versionId: string): Observable<string | boolean> {
if (!installedItem) {
return EMPTY;
return of(false);
}
return this.dialog.open(TbIotHubUpdateDialogComponent, {
return this.dialog.open<TbIotHubUpdateDialogComponent, IotHubUpdateDialogData, string | boolean>(TbIotHubUpdateDialogComponent, {
panelClass: ['tb-dialog'],
disableClose: true,
autoFocus: false,
@ -108,7 +108,7 @@ export class IotHubActionsService {
itemType: installedItem.itemType as ItemType,
version,
versionId
} as IotHubUpdateDialogData
}
}).afterClosed();
}
@ -116,15 +116,16 @@ export class IotHubActionsService {
if (!installedItem) {
return of(false);
}
return this.dialog.open(TbIotHubDeleteDialogComponent, {
return this.dialog.open<TbIotHubDeleteDialogComponent, IotHubDeleteDialogData, boolean>(TbIotHubDeleteDialogComponent, {
panelClass: ['tb-dialog'],
disableClose: true,
autoFocus: false,
data: { itemName: installedItem.itemName, itemType: installedItem.itemType } as IotHubDeleteDialogData
data: { itemName: installedItem.itemName, itemType: installedItem.itemType }
}).afterClosed().pipe(
filter(confirmed => !!confirmed),
mergeMap(() => this.iotHubApiService.deleteInstalledItem(installedItem.id.id)),
mergeMap(() => of(true))
mergeMap((confirmed) =>
confirmed
? this.iotHubApiService.deleteInstalledItem(installedItem.id.id).pipe(map(() => true))
: of(false) )
);
}

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

@ -580,8 +580,7 @@ export class TbIotHubBrowseComponent implements OnInit, AfterViewInit, OnDestroy
updateItem(item: MpItemVersionView): void {
const installedItem = this.getInstalledItem(item);
if (!installedItem) { return; }
this.iotHubActions.updateItem(installedItem, item.version, item.id as string).subscribe(result => {
this.iotHubActions.updateItem(installedItem, item.version, item.id).subscribe(result => {
if (result === 'updated') {
this.reloadInstalledItems();
}
@ -590,9 +589,10 @@ export class TbIotHubBrowseComponent implements OnInit, AfterViewInit, OnDestroy
deleteInstalledItem(item: MpItemVersionView): void {
const installedItem = this.getInstalledItem(item);
if (!installedItem) { return; }
this.iotHubActions.deleteItem(installedItem).subscribe(() => {
this.reloadInstalledItems();
this.iotHubActions.deleteItem(installedItem).subscribe((deleted) => {
if (deleted) {
this.reloadInstalledItems();
}
});
}

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

@ -157,7 +157,7 @@
<button mat-button [disabled]="resolvingPlan" (click)="cancel()">{{ 'action.cancel' | translate }}</button>
<button mat-flat-button color="primary" [disabled]="resolvingPlan" (click)="install()">
@if (resolvingPlan) {
<mat-spinner diameter="18" class="mr-2 inline-block align-middle"></mat-spinner>
<mat-spinner matButtonIcon diameter="18" class="mr-2"></mat-spinner>
}
{{ 'iot-hub.install' | translate }}
</button>
@ -169,7 +169,7 @@
[disabled]="ruleChainInstallForm.invalid || resolvingPlan"
(click)="onRuleChainInstall()">
@if (resolvingPlan) {
<mat-spinner diameter="18" class="mr-2 inline-block align-middle"></mat-spinner>
<mat-spinner matButtonIcon diameter="18" class="mr-2"></mat-spinner>
}
{{ 'iot-hub.install' | translate }}
</button>
@ -178,7 +178,7 @@
[disabled]="(activeSelectEntityConfig.required && !selectedEntityId) || resolvingPlan"
(click)="onEntitySelectInstall()">
@if (resolvingPlan) {
<mat-spinner diameter="18" class="mr-2 inline-block align-middle"></mat-spinner>
<mat-spinner matButtonIcon diameter="18" class="mr-2"></mat-spinner>
}
{{ 'iot-hub.install' | translate }}
</button>
@ -188,7 +188,7 @@
<button mat-button [disabled]="resolvingPlan" (click)="confirmOverwriteCancel()">{{ 'action.cancel' | translate }}</button>
<button mat-flat-button color="primary" [disabled]="resolvingPlan" (click)="confirmOverwriteReplace()">
@if (resolvingPlan) {
<mat-spinner diameter="18" class="mr-2 inline-block align-middle"></mat-spinner>
<mat-spinner matButtonIcon diameter="18" class="mr-2"></mat-spinner>
}
{{ 'iot-hub.rule-chain-overwrite-replace' | translate }}
</button>
@ -204,7 +204,7 @@
@case ('installing') {
<button mat-button disabled>{{ 'action.cancel' | translate }}</button>
<button mat-flat-button color="primary" disabled>
<mat-spinner diameter="18" class="mr-2 inline-block align-middle"></mat-spinner>
<mat-spinner matButtonIcon diameter="18" class="mr-2"></mat-spinner>
{{ 'iot-hub.installing' | translate }}
</button>
}

17
ui-ngx/src/app/modules/home/components/iot-hub/iot-hub-installed-items-table.component.html

@ -135,14 +135,8 @@
<tr mat-row *matRowDef="let _row; columns: displayedColumns;"></tr>
</table>
@if (isLoading) {
<div class="flex flex-1 items-center justify-center py-12">
<mat-spinner diameter="40"></mat-spinner>
</div>
}
@if (!isLoading && dataSource.length === 0) {
<div class="tb-iot-hub-empty-state flex flex-1 flex-col items-center justify-center py-20 text-center">
<div class="tb-iot-hub-empty-state flex flex-1 flex-col items-center justify-center text-center">
<div class="tb-no-data-bg"></div>
<h3>{{ 'iot-hub.no-installed-items' | translate }}</h3>
<p>{{ 'iot-hub.no-installed-items-text' | translate }}</p>
@ -166,4 +160,13 @@
(page)="onPageChange($event)"
[showFirstLastButtons]="true">
</mat-paginator>
@if (isLoading) {
<div class="absolute inset-0 backdrop-grayscale" style="background: rgba(255, 255, 255, 0.72); z-index: 100;">
<div class="flex h-full items-center justify-center">
<mat-spinner diameter="40"></mat-spinner>
</div>
</div>
}
</div>

1
ui-ngx/src/app/modules/home/components/iot-hub/iot-hub-installed-items-table.component.scss

@ -25,6 +25,7 @@
// Table + paginator container border
.tb-installed-table-container {
position: relative;
border: 1px solid rgba(0, 0, 0, 0.12);
}

10
ui-ngx/src/app/modules/home/components/iot-hub/iot-hub-installed-items-table.component.ts

@ -156,9 +156,12 @@ export class TbIotHubInstalledItemsTableComponent implements OnInit, OnChanges,
}
deleteItem(item: IotHubInstalledItem): void {
this.iotHubActions.deleteItem(item).subscribe(confirmed => {
if (confirmed) {
this.isLoading = true;
this.iotHubActions.deleteItem(item).subscribe(deleted => {
if (deleted) {
this.loadData();
} else {
this.isLoading = false;
}
});
}
@ -244,9 +247,12 @@ export class TbIotHubInstalledItemsTableComponent implements OnInit, OnChanges,
}
updateItem(item: IotHubInstalledItem, publishedInfo: ItemPublishedVersionInfo): void {
this.isLoading = true;
this.iotHubActions.updateItem(item, publishedInfo.publishedVersion, publishedInfo.publishedVersionId).subscribe(result => {
if (result === 'updated') {
this.loadData();
} else {
this.isLoading = false;
}
});
}

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

@ -220,8 +220,8 @@ export class TbIotHubItemDetailDialogComponent extends DialogComponent<TbIotHubI
}
deleteItem(): void {
this.iotHubActions.deleteItem(this.installedItem).subscribe(confirmed => {
if (confirmed) {
this.iotHubActions.deleteItem(this.installedItem).subscribe(deleted => {
if (deleted) {
this.dialogRef.close('deleted');
}
});

8
ui-ngx/src/app/modules/home/components/iot-hub/iot-hub-search.component.ts

@ -247,7 +247,6 @@ export class TbIotHubSearchComponent implements OnInit, OnDestroy {
updateItem(item: MpItemVersionView): void {
const installedItem = this.getInstalledItem(item);
if (!installedItem) { return; }
this.iotHubActions.updateItem(installedItem, item.version, item.id as string).subscribe(result => {
if (result === 'updated') {
this.reloadInstalledItems();
@ -257,9 +256,10 @@ export class TbIotHubSearchComponent implements OnInit, OnDestroy {
deleteInstalledItem(item: MpItemVersionView): void {
const installedItem = this.getInstalledItem(item);
if (!installedItem) { return; }
this.iotHubActions.deleteItem(installedItem).subscribe(() => {
this.reloadInstalledItems();
this.iotHubActions.deleteItem(installedItem).subscribe((deleted) => {
if (deleted) {
this.reloadInstalledItems();
}
});
}

2
ui-ngx/src/app/modules/home/components/iot-hub/iot-hub-update-dialog.component.html

@ -51,7 +51,7 @@
@case ('updating') {
<button mat-button disabled>{{ 'action.cancel' | translate }}</button>
<button mat-flat-button color="primary" disabled>
<mat-spinner diameter="18" class="mr-2 inline-block align-middle"></mat-spinner>
<mat-spinner matButtonIcon diameter="18" class="mr-2"></mat-spinner>
{{ 'iot-hub.updating' | translate }}
</button>
}

4
ui-ngx/src/app/modules/home/components/iot-hub/iot-hub-update-dialog.component.ts

@ -46,7 +46,7 @@ export type UpdateState = 'confirm' | 'updating' | 'success' | 'error';
templateUrl: './iot-hub-update-dialog.component.html',
styleUrls: ['./iot-hub-install-dialog.component.scss']
})
export class TbIotHubUpdateDialogComponent extends DialogComponent<TbIotHubUpdateDialogComponent> {
export class TbIotHubUpdateDialogComponent extends DialogComponent<TbIotHubUpdateDialogComponent, string | boolean> {
ItemType = ItemType;
@ -58,7 +58,7 @@ export class TbIotHubUpdateDialogComponent extends DialogComponent<TbIotHubUpdat
constructor(
protected store: Store<AppState>,
protected router: Router,
protected dialogRef: MatDialogRef<TbIotHubUpdateDialogComponent>,
protected dialogRef: MatDialogRef<TbIotHubUpdateDialogComponent, string | boolean>,
@Inject(MAT_DIALOG_DATA) public data: IotHubUpdateDialogData,
private dialog: MatDialog,
private dialogService: DialogService,

31
ui-ngx/src/app/modules/home/pages/iot-hub/iot-hub-home.component.ts

@ -381,7 +381,6 @@ export class TbIotHubHomeComponent implements OnInit, OnDestroy {
updateItem(item: MpItemVersionView): void {
const installedItem = this.findInstalledItem(item);
if (!installedItem) { return; }
this.iotHubActions.updateItem(installedItem, item.version, item.id as string).subscribe(result => {
if (result === 'updated') {
this.reloadInstalledItems(item.type);
@ -404,20 +403,22 @@ export class TbIotHubHomeComponent implements OnInit, OnDestroy {
deleteInstalledItem(item: MpItemVersionView): void {
const installedItem = this.findInstalledItem(item);
if (!installedItem) { return; }
this.iotHubActions.deleteItem(installedItem).subscribe(() => {
this.installedItemsCount = Math.max(0, this.installedItemsCount - 1);
if (item.type === ItemType.WIDGET) {
this.installedWidgets = this.installedWidgets.filter(i => i.id.id !== installedItem.id.id);
} else if (item.type === ItemType.SOLUTION_TEMPLATE) {
this.installedSolutionTemplates = this.installedSolutionTemplates.filter(i => i.id.id !== installedItem.id.id);
} else if (item.type === ItemType.DEVICE && this.installedDeviceCounts[item.itemId]) {
this.installedDeviceCounts[item.itemId] = Math.max(0, this.installedDeviceCounts[item.itemId] - 1);
} else if (item.type === ItemType.CALCULATED_FIELD && this.installedCalcFieldCounts[item.itemId]) {
this.installedCalcFieldCounts[item.itemId] = Math.max(0, this.installedCalcFieldCounts[item.itemId] - 1);
} else if (item.type === ItemType.ALARM_RULE && this.installedAlarmRuleCounts[item.itemId]) {
this.installedAlarmRuleCounts[item.itemId] = Math.max(0, this.installedAlarmRuleCounts[item.itemId] - 1);
} else if (item.type === ItemType.RULE_CHAIN && this.installedRuleChainCounts[item.itemId]) {
this.installedRuleChainCounts[item.itemId] = Math.max(0, this.installedRuleChainCounts[item.itemId] - 1);
this.iotHubActions.deleteItem(installedItem).subscribe((deleted) => {
if (deleted) {
this.installedItemsCount = Math.max(0, this.installedItemsCount - 1);
if (item.type === ItemType.WIDGET) {
this.installedWidgets = this.installedWidgets.filter(i => i.id.id !== installedItem.id.id);
} else if (item.type === ItemType.SOLUTION_TEMPLATE) {
this.installedSolutionTemplates = this.installedSolutionTemplates.filter(i => i.id.id !== installedItem.id.id);
} else if (item.type === ItemType.DEVICE && this.installedDeviceCounts[item.itemId]) {
this.installedDeviceCounts[item.itemId] = Math.max(0, this.installedDeviceCounts[item.itemId] - 1);
} else if (item.type === ItemType.CALCULATED_FIELD && this.installedCalcFieldCounts[item.itemId]) {
this.installedCalcFieldCounts[item.itemId] = Math.max(0, this.installedCalcFieldCounts[item.itemId] - 1);
} else if (item.type === ItemType.ALARM_RULE && this.installedAlarmRuleCounts[item.itemId]) {
this.installedAlarmRuleCounts[item.itemId] = Math.max(0, this.installedAlarmRuleCounts[item.itemId] - 1);
} else if (item.type === ItemType.RULE_CHAIN && this.installedRuleChainCounts[item.itemId]) {
this.installedRuleChainCounts[item.itemId] = Math.max(0, this.installedRuleChainCounts[item.itemId] - 1);
}
}
});
}

4
ui-ngx/src/app/modules/home/pages/iot-hub/iot-hub-installed-items.component.html

@ -28,9 +28,9 @@
@if (!updatesChecked) {
<button mat-flat-button color="primary" (click)="checkForUpdates()" [disabled]="isCheckingUpdates">
@if (isCheckingUpdates) {
<mat-spinner diameter="18"></mat-spinner>
<mat-spinner matButtonIcon class="mr-2" diameter="18"></mat-spinner>
} @else {
<mat-icon>update</mat-icon>
<mat-icon matButtonIcon>update</mat-icon>
}
{{ 'iot-hub.check-for-updates' | translate }}
</button>

Loading…
Cancel
Save