From f6adbf74ccede490d2c8b65faf389419680b673b Mon Sep 17 00:00:00 2001 From: Andrii Shvaika Date: Fri, 10 Apr 2026 19:08:09 +0300 Subject: [PATCH] feat(iot-hub): add setup guide button on device card in browse page MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Show a document icon button (📄) next to the "Installed" chip on device cards that have stored install state. Clicking opens the read-only review dialog with tabs (same as from Installed Items). Visible in both "same version" and "update available" card states. Tooltip: "View setup guide". Only shown for DEVICE items with installState in their descriptor. --- .../iot-hub/iot-hub-browse.component.html | 3 +- .../iot-hub/iot-hub-browse.component.ts | 31 +++++++++++++++++-- .../iot-hub/iot-hub-item-card.component.html | 12 +++++++ .../iot-hub/iot-hub-item-card.component.scss | 27 ++++++++++++++++ .../iot-hub/iot-hub-item-card.component.ts | 14 +++++++++ .../assets/locale/locale.constant-en_US.json | 1 + 6 files changed, 85 insertions(+), 3 deletions(-) diff --git a/ui-ngx/src/app/modules/home/components/iot-hub/iot-hub-browse.component.html b/ui-ngx/src/app/modules/home/components/iot-hub/iot-hub-browse.component.html index 02e4890b6e..63ccc11289 100644 --- a/ui-ngx/src/app/modules/home/components/iot-hub/iot-hub-browse.component.html +++ b/ui-ngx/src/app/modules/home/components/iot-hub/iot-hub-browse.component.html @@ -353,7 +353,8 @@ (installClick)="installItem($event)" (updateClick)="updateItem($event)" (deleteClick)="deleteInstalledItem($event)" - (addClick)="onItemAdd($event)"> + (addClick)="onItemAdd($event)" + (guideClick)="openInstallGuide($event)"> } diff --git a/ui-ngx/src/app/modules/home/components/iot-hub/iot-hub-browse.component.ts b/ui-ngx/src/app/modules/home/components/iot-hub/iot-hub-browse.component.ts index 4ae8b7c0b6..86d258c8d9 100644 --- a/ui-ngx/src/app/modules/home/components/iot-hub/iot-hub-browse.component.ts +++ b/ui-ngx/src/app/modules/home/components/iot-hub/iot-hub-browse.component.ts @@ -27,8 +27,10 @@ import { connectivityGroups, ConnectivityGroup, hardwareTypes } from '@shared/models/iot-hub/iot-hub-item.models'; import { cfTypeTranslations, widgetTypeTranslations, ruleChainTypeTranslations } from '@shared/models/iot-hub/iot-hub-version.models'; -import { IotHubInstalledItem } from '@shared/models/iot-hub/iot-hub-installed-item.models'; +import { IotHubInstalledItem, DeviceInstalledItemDescriptor } from '@shared/models/iot-hub/iot-hub-installed-item.models'; import { IotHubApiService } from '@core/http/iot-hub-api.service'; +import { TbDeviceInstallDialogComponent, DeviceInstallDialogData } from './device-install-dialog/device-install-dialog.component'; +import { MatDialog } from '@angular/material/dialog'; import { TranslateService } from '@ngx-translate/core'; import { ActivatedRoute, Router } from '@angular/router'; import { IotHubActionsService } from '@home/components/iot-hub/iot-hub-actions.service'; @@ -126,7 +128,8 @@ export class TbIotHubBrowseComponent implements OnInit, OnDestroy { private iotHubActions: IotHubActionsService, private translate: TranslateService, private router: Router, - private route: ActivatedRoute + private route: ActivatedRoute, + private dialog: MatDialog ) {} ngOnInit(): void { @@ -559,6 +562,30 @@ export class TbIotHubBrowseComponent implements OnInit, OnDestroy { }); } + openInstallGuide(item: MpItemVersionView): void { + const installedItem = this.getInstalledItem(item); + if (!installedItem) { return; } + const descriptor = installedItem.descriptor as DeviceInstalledItemDescriptor; + if (!descriptor?.installState || !descriptor?.selectedInstallMethod) { return; } + this.iotHubApiService.getVersionFileData(installedItem.itemVersionId, { ignoreLoading: true, ignoreErrors: true }).subscribe({ + next: async (blob: Blob) => { + const zipData = await blob.arrayBuffer(); + this.dialog.open(TbDeviceInstallDialogComponent, { + panelClass: ['tb-dialog', 'tb-fullscreen-dialog'], + disableClose: false, + autoFocus: false, + data: { + item, + zipData, + reviewMode: true, + selectedInstallMethod: descriptor.selectedInstallMethod, + installState: descriptor.installState + } as DeviceInstallDialogData + }); + } + }); + } + navigateToCreator(creatorId: string): void { this.router.navigate(['/iot-hub/creator', creatorId]); } diff --git a/ui-ngx/src/app/modules/home/components/iot-hub/iot-hub-item-card.component.html b/ui-ngx/src/app/modules/home/components/iot-hub/iot-hub-item-card.component.html index 66432ab1a1..ddab1451c8 100644 --- a/ui-ngx/src/app/modules/home/components/iot-hub/iot-hub-item-card.component.html +++ b/ui-ngx/src/app/modules/home/components/iot-hub/iot-hub-item-card.component.html @@ -45,11 +45,23 @@ } @else if (isInstalled()) { @if (isSameVersion()) { check{{ 'iot-hub.installed' | translate }} + @if (hasInstallGuide()) { + + } } @else { + @if (hasInstallGuide()) { + + } diff --git a/ui-ngx/src/app/modules/home/components/iot-hub/iot-hub-item-card.component.scss b/ui-ngx/src/app/modules/home/components/iot-hub/iot-hub-item-card.component.scss index e8e52c4c89..6d6415bc59 100644 --- a/ui-ngx/src/app/modules/home/components/iot-hub/iot-hub-item-card.component.scss +++ b/ui-ngx/src/app/modules/home/components/iot-hub/iot-hub-item-card.component.scss @@ -350,6 +350,33 @@ } } +// Guide icon button +.tb-iot-hub-card-guide-btn { + display: flex; + align-items: center; + justify-content: center; + width: 32px; + height: 32px; + padding: 4px; + border: none; + background: none; + cursor: pointer; + flex-shrink: 0; + border-radius: 50%; + transition: background 0.15s; + + tb-icon { + font-size: 20px; + width: 20px; + height: 20px; + color: rgba(0, 0, 0, 0.54); + } + + &:hover { + background: rgba(0, 0, 0, 0.04); + } +} + // Remove icon button — Design: trash-can-outline 24px, rgba(0,0,0,0.54) .tb-iot-hub-card-remove-btn { display: flex; diff --git a/ui-ngx/src/app/modules/home/components/iot-hub/iot-hub-item-card.component.ts b/ui-ngx/src/app/modules/home/components/iot-hub/iot-hub-item-card.component.ts index dca2813a63..83c6b54328 100644 --- a/ui-ngx/src/app/modules/home/components/iot-hub/iot-hub-item-card.component.ts +++ b/ui-ngx/src/app/modules/home/components/iot-hub/iot-hub-item-card.component.ts @@ -45,6 +45,7 @@ export class TbIotHubItemCardComponent { @Output() updateClick = new EventEmitter(); @Output() deleteClick = new EventEmitter(); @Output() addClick = new EventEmitter(); + @Output() guideClick = new EventEmitter(); typeTranslations = itemTypeTranslations; @@ -223,6 +224,19 @@ export class TbIotHubItemCardComponent { this.deleteClick.emit(this.item); } + hasInstallGuide(): boolean { + if (!this.installedItem || this.installedItem.itemType !== 'DEVICE') { + return false; + } + const descriptor = this.installedItem.descriptor as any; + return !!descriptor?.installState && !!descriptor?.selectedInstallMethod; + } + + onGuideClick(event: MouseEvent): void { + event.stopPropagation(); + this.guideClick.emit(this.item); + } + onCreatorClick(event: MouseEvent): void { event.stopPropagation(); this.creatorClick.emit(this.item.creatorId); diff --git a/ui-ngx/src/assets/locale/locale.constant-en_US.json b/ui-ngx/src/assets/locale/locale.constant-en_US.json index ad0074f20b..d3eaf96b28 100644 --- a/ui-ngx/src/assets/locale/locale.constant-en_US.json +++ b/ui-ngx/src/assets/locale/locale.constant-en_US.json @@ -3760,6 +3760,7 @@ "use-case": "Use case", "vendor": "Vendor", "vendor-placeholder": "Filter by vendor...", + "view-setup-guide": "View setup guide", "hardware-type": "Hardware Type", "connectivity": "Connectivity", "clear-all-filters": "Clear all filters",