Browse Source

Add card install/update state, itemType filter, and detail dialog image carousel

- Add @Input installedItem to card component with install/installed/update states
- Add updateClick output to card, wire update dialog from browse component
- Add itemType filter to getInstalledItems across full stack (controller → DAO)
- Fetch installed solution templates on browse page when tab selected
- Pass installedItem to detail dialog from browse component
- Reload items on install/update dialog close for solution templates
- Add MpItemVersionResource model and resources field to MpItemVersionView
- Add image carousel (ngx-hm-carousel) to detail dialog for solution templates
- Carousel: screenshots preferred over icons, autoplay 3s, dots below images
pull/15347/head
Igor Kulikov 6 months ago
parent
commit
2332445c03
  1. 5
      application/src/main/java/org/thingsboard/server/controller/IotHubController.java
  2. 2
      dao/src/main/java/org/thingsboard/server/dao/iot_hub/IotHubInstalledItemDao.java
  3. 2
      dao/src/main/java/org/thingsboard/server/dao/iot_hub/IotHubInstalledItemService.java
  4. 4
      dao/src/main/java/org/thingsboard/server/dao/iot_hub/IotHubInstalledItemServiceImpl.java
  5. 3
      dao/src/main/java/org/thingsboard/server/dao/sql/iot_hub/IotHubInstalledItemRepository.java
  6. 3
      dao/src/main/java/org/thingsboard/server/dao/sql/iot_hub/JpaIotHubInstalledItemDao.java
  7. 8
      ui-ngx/src/app/core/http/iot-hub-api.service.ts
  8. 4
      ui-ngx/src/app/modules/home/pages/iot-hub/iot-hub-browse.component.html
  9. 58
      ui-ngx/src/app/modules/home/pages/iot-hub/iot-hub-browse.component.ts
  10. 2
      ui-ngx/src/app/modules/home/pages/iot-hub/iot-hub-installed-items.component.ts
  11. 10
      ui-ngx/src/app/modules/home/pages/iot-hub/iot-hub-item-card.component.html
  12. 28
      ui-ngx/src/app/modules/home/pages/iot-hub/iot-hub-item-card.component.scss
  13. 16
      ui-ngx/src/app/modules/home/pages/iot-hub/iot-hub-item-card.component.ts
  14. 58
      ui-ngx/src/app/modules/home/pages/iot-hub/iot-hub-item-detail-dialog.component.html
  15. 84
      ui-ngx/src/app/modules/home/pages/iot-hub/iot-hub-item-detail-dialog.component.scss
  16. 16
      ui-ngx/src/app/modules/home/pages/iot-hub/iot-hub-item-detail-dialog.component.ts
  17. 22
      ui-ngx/src/app/modules/home/pages/iot-hub/iot-hub-update-dialog.component.ts
  18. 6
      ui-ngx/src/app/shared/models/iot-hub/iot-hub-version.models.ts

5
application/src/main/java/org/thingsboard/server/controller/IotHubController.java

@ -81,9 +81,10 @@ public class IotHubController extends BaseController {
@RequestParam int page,
@RequestParam(required = false) String textSearch,
@RequestParam(required = false) String sortProperty,
@RequestParam(required = false) String sortOrder) throws ThingsboardException {
@RequestParam(required = false) String sortOrder,
@RequestParam(required = false) String itemType) throws ThingsboardException {
PageLink pageLink = createPageLink(pageSize, page, textSearch, sortProperty, sortOrder);
return iotHubInstalledItemService.findByTenantId(getTenantId(), pageLink);
return iotHubInstalledItemService.findByTenantId(getTenantId(), itemType, pageLink);
}
@PreAuthorize("hasAuthority('TENANT_ADMIN')")

2
dao/src/main/java/org/thingsboard/server/dao/iot_hub/IotHubInstalledItemDao.java

@ -26,7 +26,7 @@ import java.util.UUID;
public interface IotHubInstalledItemDao extends Dao<IotHubInstalledItem> {
PageData<IotHubInstalledItem> findByTenantId(TenantId tenantId, PageLink pageLink);
PageData<IotHubInstalledItem> findByTenantId(TenantId tenantId, String itemType, PageLink pageLink);
List<UUID> findInstalledItemIdsByTenantId(TenantId tenantId);

2
dao/src/main/java/org/thingsboard/server/dao/iot_hub/IotHubInstalledItemService.java

@ -30,7 +30,7 @@ public interface IotHubInstalledItemService {
IotHubInstalledItem findById(TenantId tenantId, IotHubInstalledItemId id);
PageData<IotHubInstalledItem> findByTenantId(TenantId tenantId, PageLink pageLink);
PageData<IotHubInstalledItem> findByTenantId(TenantId tenantId, String itemType, PageLink pageLink);
List<UUID> findInstalledItemIdsByTenantId(TenantId tenantId);

4
dao/src/main/java/org/thingsboard/server/dao/iot_hub/IotHubInstalledItemServiceImpl.java

@ -46,8 +46,8 @@ class IotHubInstalledItemServiceImpl implements IotHubInstalledItemService {
}
@Override
public PageData<IotHubInstalledItem> findByTenantId(TenantId tenantId, PageLink pageLink) {
return iotHubInstalledItemDao.findByTenantId(tenantId, pageLink);
public PageData<IotHubInstalledItem> findByTenantId(TenantId tenantId, String itemType, PageLink pageLink) {
return iotHubInstalledItemDao.findByTenantId(tenantId, itemType, pageLink);
}
@Override

3
dao/src/main/java/org/thingsboard/server/dao/sql/iot_hub/IotHubInstalledItemRepository.java

@ -35,12 +35,13 @@ interface IotHubInstalledItemRepository extends JpaRepository<IotHubInstalledIte
@Query("""
SELECT item FROM IotHubInstalledItemEntity item
WHERE item.tenantId = :tenantId
AND (:itemType IS NULL OR item.itemType = :itemType)
AND (:textSearch IS NULL
OR ilike(item.itemName, CONCAT('%', :textSearch, '%')) = true
OR ilike(item.itemType, CONCAT('%', :textSearch, '%')) = true
OR ilike(item.version, CONCAT('%', :textSearch, '%')) = true)
""")
Page<IotHubInstalledItemEntity> findByTenantId(@Param("tenantId") UUID tenantId, @Param("textSearch") String textSearch, Pageable pageable);
Page<IotHubInstalledItemEntity> findByTenantId(@Param("tenantId") UUID tenantId, @Param("itemType") String itemType, @Param("textSearch") String textSearch, Pageable pageable);
@Transactional
@Modifying

3
dao/src/main/java/org/thingsboard/server/dao/sql/iot_hub/JpaIotHubInstalledItemDao.java

@ -43,9 +43,10 @@ class JpaIotHubInstalledItemDao extends JpaAbstractDao<IotHubInstalledItemEntity
private final IotHubInstalledItemRepository repository;
@Override
public PageData<IotHubInstalledItem> findByTenantId(TenantId tenantId, PageLink pageLink) {
public PageData<IotHubInstalledItem> findByTenantId(TenantId tenantId, String itemType, PageLink pageLink) {
return DaoUtil.toPageData(repository.findByTenantId(
tenantId.getId(),
itemType,
StringUtils.defaultIfEmpty(pageLink.getTextSearch(), null),
toPageRequest(pageLink))
);

8
ui-ngx/src/app/core/http/iot-hub-api.service.ts

@ -139,9 +139,13 @@ export class IotHubApiService {
);
}
public getInstalledItems(pageLink: PageLink, config?: IotHubRequestConfig): Observable<PageData<IotHubInstalledItem>> {
public getInstalledItems(pageLink: PageLink, itemType?: string, config?: IotHubRequestConfig): Observable<PageData<IotHubInstalledItem>> {
let query = pageLink.toQuery();
if (itemType) {
query += `&itemType=${itemType}`;
}
return this.http.get<PageData<IotHubInstalledItem>>(
`/api/iot-hub/installedItems${pageLink.toQuery()}`,
`/api/iot-hub/installedItems${query}`,
{ params: this.buildParams(config) }
);
}

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

@ -196,11 +196,13 @@
@for (item of items; track item.id) {
<tb-iot-hub-item-card
[item]="item"
[installedItem]="getInstalledItem(item)"
[showTypeChip]="false"
[showCreator]="!creatorId"
(cardClick)="openItemDetail($event)"
(creatorClick)="navigateToCreator($event)"
(installClick)="installItem($event)">
(installClick)="installItem($event)"
(updateClick)="updateItem($event)">
</tb-iot-hub-item-card>
}
</div>

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

@ -26,12 +26,14 @@ import {
getCategoriesForType, useCaseTranslations
} 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 { IotHubApiService } from '@core/http/iot-hub-api.service';
import { MatDialog } from '@angular/material/dialog';
import { TranslateService } from '@ngx-translate/core';
import { Router } from '@angular/router';
import { TbIotHubItemDetailDialogComponent, IotHubItemDetailDialogData } from './iot-hub-item-detail-dialog.component';
import { TbIotHubInstallDialogComponent, IotHubInstallDialogData } from './iot-hub-install-dialog.component';
import { TbIotHubUpdateDialogComponent, IotHubUpdateDialogData } from './iot-hub-update-dialog.component';
interface SortOption {
value: string;
@ -90,6 +92,8 @@ export class TbIotHubBrowseComponent implements OnInit, OnDestroy {
widgetTypes: Map<string, string> = widgetTypeTranslations;
ruleChainTypes: Map<string, string> = ruleChainTypeTranslations;
installedSolutionTemplates: IotHubInstalledItem[] = null;
private searchSubject = new Subject<string>();
private destroy$ = new Subject<void>();
@ -139,6 +143,9 @@ export class TbIotHubBrowseComponent implements OnInit, OnDestroy {
this.activeRuleChainTypes.clear();
this.updateCategories();
this.pageIndex = 0;
if (type === ItemType.SOLUTION_TEMPLATE) {
this.loadInstalledSolutionTemplates();
}
this.loadItems();
}
@ -326,27 +333,62 @@ export class TbIotHubBrowseComponent implements OnInit, OnDestroy {
}
}
getInstalledItem(item: MpItemVersionView): IotHubInstalledItem | undefined {
if (this.activeType !== ItemType.SOLUTION_TEMPLATE || !this.installedSolutionTemplates) {
return undefined;
}
return this.installedSolutionTemplates.find(i => i.itemId === item.itemId);
}
openItemDetail(item: MpItemVersionView): void {
this.dialog.open(TbIotHubItemDetailDialogComponent, {
panelClass: ['tb-dialog', 'tb-fullscreen-dialog'],
autoFocus: false,
data: {
item,
iotHubApiService: this.iotHubApiService
iotHubApiService: this.iotHubApiService,
installedItem: this.getInstalledItem(item)
} as IotHubItemDetailDialogData
});
}
installItem(item: MpItemVersionView): void {
this.dialog.open(TbIotHubInstallDialogComponent, {
const dialogRef = this.dialog.open(TbIotHubInstallDialogComponent, {
panelClass: ['tb-dialog'],
data: {
item,
iotHubApiService: this.iotHubApiService
} as IotHubInstallDialogData
});
dialogRef.afterClosed().subscribe(result => {
if (result === 'installed' && this.activeType === ItemType.SOLUTION_TEMPLATE) {
this.loadItems();
}
});
}
updateItem(item: MpItemVersionView): void {
const installedItem = this.getInstalledItem(item);
if (!installedItem) {
return;
}
const dialogRef = this.dialog.open(TbIotHubUpdateDialogComponent, {
panelClass: ['tb-dialog'],
data: {
installedItemId: installedItem.id.id,
itemName: item.name,
itemType: item.type,
version: item.version,
versionId: item.id,
iotHubApiService: this.iotHubApiService
} as IotHubUpdateDialogData
});
dialogRef.afterClosed().subscribe(result => {
if (result === 'updated' && this.activeType === ItemType.SOLUTION_TEMPLATE) {
this.loadItems();
}
});
}
navigateToCreator(creatorId: string): void {
this.router.navigate(['/iot-hub/creator', creatorId]);
@ -356,6 +398,18 @@ export class TbIotHubBrowseComponent implements OnInit, OnDestroy {
this.router.navigate(['/iot-hub/installed']);
}
private loadInstalledSolutionTemplates(): void {
if (this.installedSolutionTemplates !== null) {
return;
}
const pageLink = new PageLink(10000, 0);
this.iotHubApiService.getInstalledItems(pageLink, ItemType.SOLUTION_TEMPLATE, {ignoreLoading: true}).subscribe({
next: (data) => {
this.installedSolutionTemplates = data.data;
}
});
}
private updateCategories(): void {
this.categories = getCategoriesForType(this.activeType);
}

2
ui-ngx/src/app/modules/home/pages/iot-hub/iot-hub-installed-items.component.ts

@ -258,7 +258,7 @@ export class TbIotHubInstalledItemsComponent implements OnInit, AfterViewInit {
direction: this.sort?.direction === 'asc' ? Direction.ASC : Direction.DESC
};
const pageLink = new PageLink(this.pageSize, this.pageIndex, this.textSearch || null, sortOrder);
this.iotHubApiService.getInstalledItems(pageLink, {ignoreLoading: true}).subscribe({
this.iotHubApiService.getInstalledItems(pageLink, null, {ignoreLoading: true}).subscribe({
next: (data) => {
this.dataSource = data.data;
this.totalElements = data.totalElements;

10
ui-ngx/src/app/modules/home/pages/iot-hub/iot-hub-item-card.component.html

@ -100,7 +100,15 @@
<tb-icon>download</tb-icon>
{{ item.totalInstallCount | shortNumber }}
</span>
<button class="tb-iot-hub-card-install-btn" (click)="onInstallClick($event)">{{ 'iot-hub.install' | translate }}</button>
@if (isInstalled()) {
@if (isSameVersion()) {
<span class="tb-iot-hub-card-installed-label">{{ 'iot-hub.installed' | translate }}</span>
} @else {
<button class="tb-iot-hub-card-update-btn" (click)="onUpdateClick($event)">{{ 'iot-hub.update' | translate }}</button>
}
} @else {
<button class="tb-iot-hub-card-install-btn" (click)="onInstallClick($event)">{{ 'iot-hub.install' | translate }}</button>
}
</span>
</div>
</div>

28
ui-ngx/src/app/modules/home/pages/iot-hub/iot-hub-item-card.component.scss

@ -349,4 +349,32 @@
}
}
.tb-iot-hub-card-installed-label {
padding: 5px 14px;
border-radius: 6px;
background: #e8f5e9;
color: #2e7d32;
font-size: 12px;
font-weight: 600;
white-space: nowrap;
}
.tb-iot-hub-card-update-btn {
padding: 5px 14px;
border-radius: 6px;
border: 1px solid #f57c00;
background: transparent;
color: #f57c00;
font-size: 12px;
font-weight: 600;
cursor: pointer;
white-space: nowrap;
transition: all 0.15s;
&:hover {
background: #f57c00;
color: #fff;
}
}
}

16
ui-ngx/src/app/modules/home/pages/iot-hub/iot-hub-item-card.component.ts

@ -17,6 +17,7 @@
import { Component, EventEmitter, Input, Output } from '@angular/core';
import { MpItemVersionView, cfTypeTranslations, cfTypeIcons, ruleChainTypeTranslations, widgetTypeTranslations, NodeInfo, nodeComponentTypeTranslations } from '@shared/models/iot-hub/iot-hub-version.models';
import { ItemType, itemTypeTranslations, getCategoriesForType, useCaseTranslations } from '@shared/models/iot-hub/iot-hub-item.models';
import { IotHubInstalledItem } from '@shared/models/iot-hub/iot-hub-installed-item.models';
import { TranslateService } from '@ngx-translate/core';
import { IotHubApiService } from '@core/http/iot-hub-api.service';
@ -33,11 +34,13 @@ export class TbIotHubItemCardComponent {
readonly maxVisibleNodes = 2;
@Input() item: MpItemVersionView;
@Input() installedItem: IotHubInstalledItem;
@Input() showCreator = true;
@Input() showTypeChip = true;
@Output() cardClick = new EventEmitter<MpItemVersionView>();
@Output() creatorClick = new EventEmitter<string>();
@Output() installClick = new EventEmitter<MpItemVersionView>();
@Output() updateClick = new EventEmitter<MpItemVersionView>();
typeTranslations = itemTypeTranslations;
@ -184,6 +187,14 @@ export class TbIotHubItemCardComponent {
}
}
isInstalled(): boolean {
return !!this.installedItem;
}
isSameVersion(): boolean {
return this.installedItem?.itemVersionId === this.item.id;
}
onCardClick(): void {
this.cardClick.emit(this.item);
}
@ -193,6 +204,11 @@ export class TbIotHubItemCardComponent {
this.installClick.emit(this.item);
}
onUpdateClick(event: MouseEvent): void {
event.stopPropagation();
this.updateClick.emit(this.item);
}
onCreatorClick(event: MouseEvent): void {
event.stopPropagation();
this.creatorClick.emit(this.item.creatorId);

58
ui-ngx/src/app/modules/home/pages/iot-hub/iot-hub-item-detail-dialog.component.html

@ -69,8 +69,8 @@
</div>
}
<!-- Dashboard / Solution Template: full-width image + description below -->
@if (item.type === ItemType.DASHBOARD || item.type === ItemType.SOLUTION_TEMPLATE) {
<!-- Dashboard: full-width image + description below -->
@if (item.type === ItemType.DASHBOARD) {
<div class="dlg-hero-full">
<div class="dlg-hero-full-image">
@if (getPreviewUrl(); as url) {
@ -87,6 +87,60 @@
</div>
}
<!-- Solution Template: image carousel + description below -->
@if (item.type === ItemType.SOLUTION_TEMPLATE) {
<div class="dlg-hero-full">
@if (carouselImages.length > 1) {
<div class="dlg-hero-carousel">
<ngx-hm-carousel
[(ngModel)]="carouselIndex"
[data]="carouselImages"
[infinite]="true"
[autoplay-speed]="3000"
[between-delay]="0"
[autoplay]="true"
[mouse-enable]="true"
class="dlg-carousel">
<section ngx-hm-carousel-container class="dlg-carousel-container">
<article class="dlg-carousel-item"
ngx-hm-carousel-item
*ngFor="let imageUrl of carouselImages">
<img [src]="imageUrl" alt="">
</article>
</section>
<ng-template #carouselPrev>
<button mat-icon-button class="dlg-carousel-nav dlg-carousel-prev">
<mat-icon>chevron_left</mat-icon>
</button>
</ng-template>
<ng-template #carouselNext>
<button mat-icon-button class="dlg-carousel-nav dlg-carousel-next">
<mat-icon>chevron_right</mat-icon>
</button>
</ng-template>
<ng-template #carouselDot let-model>
<div class="dlg-carousel-dot"
[class.active]="model.index === model.currentIndex"></div>
</ng-template>
</ngx-hm-carousel>
</div>
} @else {
<div class="dlg-hero-full-image">
@if (getPreviewUrl(); as url) {
<img [src]="url" alt="">
} @else {
<div class="tb-iot-hub-preview-placeholder">
<tb-icon>{{ getTypeIcon() }}</tb-icon>
</div>
}
</div>
}
@if (item.description) {
<div class="dlg-hero-full-desc">{{ item.description }}</div>
}
</div>
}
<!-- Stats bar -->
<div class="dlg-stats-bar">
<span class="dlg-stat">

84
ui-ngx/src/app/modules/home/pages/iot-hub/iot-hub-item-detail-dialog.component.scss

@ -215,6 +215,90 @@
line-height: 1.6;
}
// Image carousel
.dlg-hero-carousel {
background: #f5f7fa;
border-radius: 8px;
overflow: hidden;
position: relative;
height: 340px;
animation: dlg-carousel-fade-in 0s 0.3s forwards;
visibility: hidden;
}
@keyframes dlg-carousel-fade-in {
to {
visibility: visible;
}
}
.dlg-carousel {
height: 340px;
::ng-deep .carousel {
display: flex !important;
flex-direction: column !important;
> section {
flex: 1;
min-height: 0;
}
ul.indicators {
position: relative !important;
bottom: auto !important;
flex-shrink: 0;
padding: 8px 0;
}
.direction {
height: calc(100% - 40px) !important;
}
}
.dlg-carousel-container {
display: flex;
height: 100%;
.dlg-carousel-item {
position: relative;
overflow: hidden;
img {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
object-fit: contain;
padding: 8px;
}
}
}
}
.dlg-carousel-nav {
color: rgba(0, 0, 0, 0.6);
background: rgba(255, 255, 255, 0.8);
&:hover {
background: rgba(255, 255, 255, 0.95);
}
}
.dlg-carousel-dot {
width: 8px;
height: 8px;
border-radius: 50%;
background: rgba(0, 0, 0, 0.2);
margin: 0 3px;
transition: background 0.2s;
&.active {
background: rgba(0, 0, 0, 0.6);
}
}
// Stats bar
.dlg-stats-bar {
display: flex;

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

@ -48,6 +48,8 @@ export class TbIotHubItemDetailDialogComponent {
typeTranslations = itemTypeTranslations;
readmeContent: string = '';
installedItem?: IotHubInstalledItem;
carouselImages: string[] = [];
carouselIndex = 0;
private categoryMap: Map<string, string>;
private useCaseMap = useCaseTranslations;
@ -62,6 +64,7 @@ export class TbIotHubItemDetailDialogComponent {
this.item = data.item;
this.installedItem = data.installedItem;
this.categoryMap = getCategoriesForType(this.item.type);
this.buildCarouselImages();
this.loadReadme();
}
@ -321,6 +324,19 @@ export class TbIotHubItemDetailDialogComponent {
this.dialogRef.close();
}
private buildCarouselImages(): void {
if (this.item.type !== ItemType.SOLUTION_TEMPLATE || !this.item.resources?.length) {
return;
}
const screenshotResources = this.item.resources.filter(r => r.type === 'SCREENSHOT');
const allResources = screenshotResources.length > 0
? screenshotResources
: this.item.resources.filter(r => r.type === 'ICON');
this.carouselImages = allResources.map(r =>
this.data.iotHubApiService.resolveResourceUrl(`/api/resources/${r.id}`)
);
}
private loadReadme(): void {
const versionId = this.item.id as string;
this.data.iotHubApiService.getVersionReadme(versionId, { ignoreLoading: true }).subscribe(

22
ui-ngx/src/app/modules/home/pages/iot-hub/iot-hub-update-dialog.component.ts

@ -15,15 +15,19 @@
///
import { Component, Inject } from '@angular/core';
import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog';
import { MAT_DIALOG_DATA, MatDialog, MatDialogRef } from '@angular/material/dialog';
import { Router } from '@angular/router';
import { ItemType, itemTypeTranslations } from '@shared/models/iot-hub/iot-hub-item.models';
import { IotHubInstalledItemDescriptor } from '@shared/models/iot-hub/iot-hub-installed-item.models';
import {
IotHubInstalledItemDescriptor,
SolutionTemplateInstalledItemDescriptor
} from '@shared/models/iot-hub/iot-hub-installed-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 { EntityType } from '@shared/models/entity-type.models';
import { getEntityDetailsPageURL } from '@core/utils';
import { SolutionInstallDialogComponent } from '@home/components/solution/solution-install-dialog.component';
export interface IotHubUpdateDialogData {
installedItemId: string;
@ -151,6 +155,7 @@ export class TbIotHubUpdateDialogComponent {
constructor(
@Inject(MAT_DIALOG_DATA) public data: IotHubUpdateDialogData,
private dialogRef: MatDialogRef<TbIotHubUpdateDialogComponent>,
private dialog: MatDialog,
private dialogService: DialogService,
private router: Router,
private translate: TranslateService
@ -166,8 +171,17 @@ export class TbIotHubUpdateDialogComponent {
this.data.iotHubApiService.updateItemVersion(this.data.installedItemId, this.data.versionId, { ignoreLoading: true }, force).subscribe({
next: (result) => {
if (result.success) {
this.state = 'success';
this.entityDetailsUrl = this.resolveEntityDetailsUrl(result.descriptor);
if (result.descriptor?.type === 'SOLUTION_TEMPLATE') {
this.dialogRef.close('updated');
this.dialog.open(SolutionInstallDialogComponent, {
disableClose: true,
panelClass: 'tb-solution-install-dialog-panel',
data: { descriptor: result.descriptor as SolutionTemplateInstalledItemDescriptor }
});
} else {
this.state = 'success';
this.entityDetailsUrl = this.resolveEntityDetailsUrl(result.descriptor);
}
} else if (result.entityModified) {
this.state = 'confirm';
this.dialogService.confirm(

6
ui-ngx/src/app/shared/models/iot-hub/iot-hub-version.models.ts

@ -77,6 +77,11 @@ export const ruleChainTypeTranslations = new Map<string, string>([
['EDGE', 'item-data.rule-chain-type-edge'],
]);
export interface MpItemVersionResource {
id: string;
type: string;
}
export interface MpItemVersionView {
id: string;
createdTime: number;
@ -103,6 +108,7 @@ export interface MpItemVersionView {
creatorAvatarUrl: string;
installCount: number;
totalInstallCount: number;
resources: MpItemVersionResource[];
}
export class MpItemVersionQuery {

Loading…
Cancel
Save