Browse Source

Add IoT Hub widgets integration to dashboard widget select

- Move item detail dialog to home/components/iot-hub for shared access
- Register in HomeComponentsModule (declarations + exports)
- Add 'add' mode to item detail dialog showing "Add" button instead of install
- Add 'iotHub' mode to dashboard-widget-select with IoT Hub widget grid
- IoT Hub cards show author + install count footer
- Clicking card opens detail dialog in 'add' mode
- Add installs widget via IoT Hub API then emits widgetSelected with full type info
- Add "IoT Hub" option to mode toggle in dashboard page header
- Hide filter/deprecated controls in IoT Hub mode
- Hero keywords clickable to navigate to items pages
pull/15508/head
Igor Kulikov 6 months ago
parent
commit
7229b880ae
  1. 12
      ui-ngx/src/app/modules/home/components/dashboard-page/dashboard-page.component.html
  2. 68
      ui-ngx/src/app/modules/home/components/dashboard-page/dashboard-widget-select.component.html
  3. 40
      ui-ngx/src/app/modules/home/components/dashboard-page/dashboard-widget-select.component.scss
  4. 78
      ui-ngx/src/app/modules/home/components/dashboard-page/dashboard-widget-select.component.ts
  5. 3
      ui-ngx/src/app/modules/home/components/home-components.module.ts
  6. 64
      ui-ngx/src/app/modules/home/components/iot-hub/iot-hub-item-detail-dialog.component.html
  7. 0
      ui-ngx/src/app/modules/home/components/iot-hub/iot-hub-item-detail-dialog.component.scss
  8. 17
      ui-ngx/src/app/modules/home/components/iot-hub/iot-hub-item-detail-dialog.component.ts
  9. 2
      ui-ngx/src/app/modules/home/pages/iot-hub/iot-hub-browse.component.ts
  10. 3
      ui-ngx/src/app/modules/home/pages/iot-hub/iot-hub-home.component.html
  11. 2
      ui-ngx/src/app/modules/home/pages/iot-hub/iot-hub-home.component.ts
  12. 2
      ui-ngx/src/app/modules/home/pages/iot-hub/iot-hub-installed-items.component.ts
  13. 2
      ui-ngx/src/app/modules/home/pages/iot-hub/iot-hub-search.component.ts
  14. 2
      ui-ngx/src/app/modules/home/pages/iot-hub/iot-hub.module.ts
  15. 1
      ui-ngx/src/assets/locale/locale.constant-en_US.json

12
ui-ngx/src/app/modules/home/components/dashboard-page/dashboard-page.component.html

@ -402,7 +402,8 @@
</tb-details-panel> </tb-details-panel>
<tb-details-panel *ngIf="!isAddingWidgetClosed && !widgetEditMode" class="flex-1" <tb-details-panel *ngIf="!isAddingWidgetClosed && !widgetEditMode" class="flex-1"
headerTitle="{{ isAddingWidget ? headerTitle="{{ isAddingWidget ?
((dashboardWidgetSelectComponent?.selectWidgetMode === 'allWidgets' ? ('dashboard.select-widget-value' | translate: { title: ('widget.all-widgets' | translate) }) : (dashboardWidgetSelectComponent?.selectWidgetMode === 'iotHub' ? ('iot-hub.add-widget-from-iot-hub' | translate) :
(dashboardWidgetSelectComponent?.selectWidgetMode === 'allWidgets' ? ('dashboard.select-widget-value' | translate: { title: ('widget.all-widgets' | translate) }) :
(!dashboardWidgetSelectComponent?.widgetsBundle ? (!dashboardWidgetSelectComponent?.widgetsBundle ?
'widget.select-widgets-bundle' : 'dashboard.select-widget-value') | translate: dashboardWidgetSelectComponent?.widgetsBundle)) : '' 'widget.select-widgets-bundle' : 'dashboard.select-widget-value') | translate: dashboardWidgetSelectComponent?.widgetsBundle)) : ''
}}" }}"
@ -413,7 +414,7 @@
backgroundColor="#cfd8dc" backgroundColor="#cfd8dc"
(closeDetails)="onAddWidgetClosed()" (closeDetails)="onAddWidgetClosed()"
(closeSearch)="dashboardWidgetSelectComponent.search = ''"> (closeSearch)="dashboardWidgetSelectComponent.search = ''">
<div class="prefix-title-buttons" [class.!hidden]="!(isAddingWidget && (dashboardWidgetSelectComponent?.widgetsBundle || dashboardWidgetSelectComponent?.selectWidgetMode === 'allWidgets'))" style="height: 28px; margin-right: 12px"> <div class="prefix-title-buttons" [class.!hidden]="!(isAddingWidget && (dashboardWidgetSelectComponent?.widgetsBundle || dashboardWidgetSelectComponent?.selectWidgetMode === 'allWidgets' || dashboardWidgetSelectComponent?.selectWidgetMode === 'iotHub'))" style="height: 28px; margin-right: 12px">
<button class="tb-mat-28" mat-icon-button type="button" (click)="clearSelectedWidgetBundle()"> <button class="tb-mat-28" mat-icon-button type="button" (click)="clearSelectedWidgetBundle()">
<mat-icon>arrow_back</mat-icon> <mat-icon>arrow_back</mat-icon>
</button> </button>
@ -429,22 +430,23 @@
<button mat-button type="button" (click)="importWidget($event)" <button mat-button type="button" (click)="importWidget($event)"
*ngIf="dashboardWidgetSelectComponent?.selectWidgetMode === 'bundles' && !dashboardWidgetSelectComponent?.widgetsBundle"> *ngIf="dashboardWidgetSelectComponent?.selectWidgetMode === 'bundles' && !dashboardWidgetSelectComponent?.widgetsBundle">
<mat-icon>file_upload</mat-icon>{{ 'dashboard.import-widget' | translate }}</button> <mat-icon>file_upload</mat-icon>{{ 'dashboard.import-widget' | translate }}</button>
<tb-toggle-select *ngIf="dashboardWidgetSelectComponent?.selectWidgetMode === 'bundles' && !dashboardWidgetSelectComponent?.widgetsBundle" <tb-toggle-select *ngIf="dashboardWidgetSelectComponent?.selectWidgetMode !== 'iotHub' && (dashboardWidgetSelectComponent?.selectWidgetMode === 'bundles' && !dashboardWidgetSelectComponent?.widgetsBundle)"
appearance="fill-invert" appearance="fill-invert"
disablePagination disablePagination
selectMediaBreakpoint="xs" selectMediaBreakpoint="xs"
[(ngModel)]="dashboardWidgetSelectComponent.selectWidgetMode"> [(ngModel)]="dashboardWidgetSelectComponent.selectWidgetMode">
<tb-toggle-option value="bundles">{{ 'widgets-bundle.widgets-bundles' | translate }}</tb-toggle-option> <tb-toggle-option value="bundles">{{ 'widgets-bundle.widgets-bundles' | translate }}</tb-toggle-option>
<tb-toggle-option value="allWidgets">{{ 'widget.all-widgets' | translate }}</tb-toggle-option> <tb-toggle-option value="allWidgets">{{ 'widget.all-widgets' | translate }}</tb-toggle-option>
<tb-toggle-option value="iotHub">{{ 'iot-hub.iot-hub' | translate }}</tb-toggle-option>
</tb-toggle-select> </tb-toggle-select>
<button mat-icon-button type="button" <button mat-icon-button type="button"
*ngIf="dashboardWidgetSelectComponent?.widgetTypes.size > 1" *ngIf="dashboardWidgetSelectComponent?.selectWidgetMode !== 'iotHub' && dashboardWidgetSelectComponent?.widgetTypes.size > 1"
(click)="editWidgetsTypesToDisplay($event)" (click)="editWidgetsTypesToDisplay($event)"
matTooltip="{{ 'widget.filter' | translate }}" matTooltip="{{ 'widget.filter' | translate }}"
matTooltipPosition="above"> matTooltipPosition="above">
<mat-icon>filter_list</mat-icon> <mat-icon>filter_list</mat-icon>
</button> </button>
<tb-toggle-select *ngIf="dashboardWidgetSelectComponent?.hasDeprecated" <tb-toggle-select *ngIf="dashboardWidgetSelectComponent?.selectWidgetMode !== 'iotHub' && dashboardWidgetSelectComponent?.hasDeprecated"
appearance="fill-invert" appearance="fill-invert"
disablePagination disablePagination
selectMediaBreakpoint="xs" selectMediaBreakpoint="xs"

68
ui-ngx/src/app/modules/home/components/dashboard-page/dashboard-widget-select.component.html

@ -16,13 +16,18 @@
--> -->
<div class="tb-dashboard-widget-select"> <div class="tb-dashboard-widget-select">
<ng-container *ngIf="selectWidgetMode === 'bundles'; else allWidgets"> <ng-container *ngIf="selectWidgetMode === 'iotHub'; else standardModes">
<ng-container *ngIf="widgetsBundle; else bundles"> <ng-container *ngTemplateOutlet="iotHubWidgets"></ng-container>
<ng-container *ngTemplateOutlet="widgets; context:{ fetchFunction: widgetsFetchFunction, filter: widgetsFilter }"></ng-container>
</ng-container>
</ng-container> </ng-container>
<ng-template #allWidgets> <ng-template #standardModes>
<ng-container *ngTemplateOutlet="widgets; context:{ fetchFunction: allWidgetsFetchFunction, filter: allWidgetsFilter }"></ng-container> <ng-container *ngIf="selectWidgetMode === 'bundles'; else allWidgets">
<ng-container *ngIf="widgetsBundle; else bundles">
<ng-container *ngTemplateOutlet="widgets; context:{ fetchFunction: widgetsFetchFunction, filter: widgetsFilter }"></ng-container>
</ng-container>
</ng-container>
<ng-template #allWidgets>
<ng-container *ngTemplateOutlet="widgets; context:{ fetchFunction: allWidgetsFetchFunction, filter: allWidgetsFilter }"></ng-container>
</ng-template>
</ng-template> </ng-template>
<ng-template #widgets let-fetchFunction="fetchFunction" let-filter="filter"> <ng-template #widgets let-fetchFunction="fetchFunction" let-filter="filter">
<tb-scroll-grid <tb-scroll-grid
@ -118,4 +123,55 @@
</div> </div>
</mat-card> </mat-card>
</ng-template> </ng-template>
<ng-template #iotHubWidgets>
<tb-scroll-grid
[columns]="columns"
[itemSize]="gridWidgetsItemSizeStrategy"
[fetchFunction]="iotHubWidgetsFetchFunction"
[filter]="iotHubWidgetsFilter"
[itemCard]="iotHubWidgetCard"
[loadingCell]="widgetLoadingCard"
[dataLoading]="loadingIotHubWidgets"
[noData]="noIotHubWidgets">
</tb-scroll-grid>
<ng-template #iotHubWidgetCard let-item="item">
<mat-card class="tb-widget-preview-card tb-iot-hub-widget-card flex flex-col gap-2" appearance="raised" (click)="onIotHubWidgetClicked($event, item)">
<div class="title-container">
<div title="{{item.name}}" class="widget-title">
{{item.name}}
</div>
<div class="title-items">
<div tb-popover [tbPopoverContent]="item.description" [tbPopoverOverlayStyle]="{maxWidth: '300px'}" tbPopoverShowCloseButton="false" class="info-banner tb-primary-fill"><span>i</span></div>
</div>
</div>
<div class="preview-container">
<div class="preview-spacer"></div>
<img *ngIf="getIotHubItemImage(item) as imgUrl" class="preview" [src]="imgUrl" alt="{{ item.name }}">
<img *ngIf="!getIotHubItemImage(item)" class="preview" src="/assets/widget-preview-empty.svg" alt="{{ item.name }}">
</div>
<div class="tb-iot-hub-widget-footer">
<span class="tb-iot-hub-widget-author">
<mat-icon>person</mat-icon>
{{ item.creatorDisplayName }}
</span>
<span class="tb-iot-hub-widget-installs">
<mat-icon>file_download</mat-icon>
{{ item.totalInstallCount | shortNumber }}
</span>
</div>
</mat-card>
</ng-template>
<ng-template #loadingIotHubWidgets>
<div class="tb-absolute-fill flex flex-col items-center justify-center">
<span class="mat-headline-5" style="padding-bottom: 20px;">
{{ 'widget.loading-widgets' | translate }}
</span>
<mat-spinner color="accent" strokeWidth="5"></mat-spinner>
</div>
</ng-template>
<ng-template #noIotHubWidgets>
<span class="mat-headline-5 tb-absolute-fill flex items-center justify-center">{{ 'iot-hub.no-items-found' | translate }}</span>
</ng-template>
</ng-template>
</div> </div>

40
ui-ngx/src/app/modules/home/components/dashboard-page/dashboard-widget-select.component.scss

@ -139,6 +139,46 @@
} }
} }
// IoT Hub widget card footer
.tb-iot-hub-widget-card {
.tb-iot-hub-widget-footer {
display: flex;
align-items: center;
justify-content: space-between;
padding-top: 4px;
}
.tb-iot-hub-widget-author,
.tb-iot-hub-widget-installs {
display: flex;
align-items: center;
gap: 4px;
font-size: 12px;
font-weight: 400;
line-height: 16px;
letter-spacing: 0.25px;
color: rgba(0, 0, 0, 0.54);
mat-icon {
font-size: 16px;
width: 16px;
height: 16px;
color: rgba(0, 0, 0, 0.54);
}
}
.tb-iot-hub-widget-installs {
font-weight: 500;
mat-icon {
font-size: 12px;
width: 12px;
height: 12px;
}
}
}
}
@keyframes shine { @keyframes shine {
to { to {
background-position-x: -200%; background-position-x: -200%;

78
ui-ngx/src/app/modules/home/components/dashboard-page/dashboard-widget-select.component.ts

@ -30,12 +30,20 @@ import { debounceTime, distinctUntilChanged, map, skip } from 'rxjs/operators';
import { BehaviorSubject, combineLatest } from 'rxjs'; import { BehaviorSubject, combineLatest } from 'rxjs';
import { isObject } from '@core/utils'; import { isObject } from '@core/utils';
import { PageLink } from '@shared/models/page/page-link'; import { PageLink } from '@shared/models/page/page-link';
import { Direction } from '@shared/models/page/sort-order'; import { Direction, SortOrder } from '@shared/models/page/sort-order';
import { GridEntitiesFetchFunction, ScrollGridColumns } from '@shared/components/grid/scroll-grid-datasource'; import { GridEntitiesFetchFunction, ScrollGridColumns } from '@shared/components/grid/scroll-grid-datasource';
import { ItemSizeStrategy } from '@shared/components/grid/scroll-grid.component'; import { ItemSizeStrategy } from '@shared/components/grid/scroll-grid.component';
import { coerceBoolean } from '@shared/decorators/coercion'; import { coerceBoolean } from '@shared/decorators/coercion';
import { MatDialog } from '@angular/material/dialog';
import { MpItemVersionQuery, MpItemVersionView } from '@shared/models/iot-hub/iot-hub-version.models';
import { ItemType } from '@shared/models/iot-hub/iot-hub-item.models';
import { IotHubApiService } from '@core/http/iot-hub-api.service';
import {
TbIotHubItemDetailDialogComponent,
IotHubItemDetailDialogData
} from '@home/components/iot-hub/iot-hub-item-detail-dialog.component';
type selectWidgetMode = 'bundles' | 'allWidgets'; type selectWidgetMode = 'bundles' | 'allWidgets' | 'iotHub';
interface WidgetsFilter { interface WidgetsFilter {
search: string; search: string;
@ -163,12 +171,16 @@ export class DashboardWidgetSelectComponent implements OnInit {
widgetBundlesFetchFunction: GridEntitiesFetchFunction<WidgetsBundle, string>; widgetBundlesFetchFunction: GridEntitiesFetchFunction<WidgetsBundle, string>;
allWidgetsFetchFunction: GridEntitiesFetchFunction<WidgetTypeInfo, WidgetsFilter>; allWidgetsFetchFunction: GridEntitiesFetchFunction<WidgetTypeInfo, WidgetsFilter>;
widgetsFetchFunction: GridEntitiesFetchFunction<WidgetTypeInfo, BundleWidgetsFilter>; widgetsFetchFunction: GridEntitiesFetchFunction<WidgetTypeInfo, BundleWidgetsFilter>;
iotHubWidgetsFetchFunction: GridEntitiesFetchFunction<MpItemVersionView, string>;
widgetsBundleFilter = ''; widgetsBundleFilter = '';
allWidgetsFilter: WidgetsFilter = {search: '', filter: null, deprecatedFilter: DeprecatedFilter.ACTUAL}; allWidgetsFilter: WidgetsFilter = {search: '', filter: null, deprecatedFilter: DeprecatedFilter.ACTUAL};
widgetsFilter: BundleWidgetsFilter = {search: '', filter: null, deprecatedFilter: DeprecatedFilter.ACTUAL, widgetsBundleId: null}; widgetsFilter: BundleWidgetsFilter = {search: '', filter: null, deprecatedFilter: DeprecatedFilter.ACTUAL, widgetsBundleId: null};
iotHubWidgetsFilter = '';
constructor(private widgetsService: WidgetService, constructor(private widgetsService: WidgetService,
private iotHubApiService: IotHubApiService,
private dialog: MatDialog,
private cd: ChangeDetectorRef) { private cd: ChangeDetectorRef) {
this.widgetBundlesFetchFunction = (pageSize, page, filter) => { this.widgetBundlesFetchFunction = (pageSize, page, filter) => {
@ -197,12 +209,20 @@ export class DashboardWidgetSelectComponent implements OnInit {
true, filter.deprecatedFilter, filter.filter); true, filter.deprecatedFilter, filter.filter);
}; };
this.iotHubWidgetsFetchFunction = (pageSize, page, filter) => {
const sortOrder: SortOrder = { property: 'totalInstallCount', direction: Direction.DESC };
const pageLink = new PageLink(pageSize, page, filter || null, sortOrder);
const query = new MpItemVersionQuery(pageLink, ItemType.WIDGET);
return this.iotHubApiService.getPublishedVersions(query, { ignoreLoading: true });
};
this.search$.pipe( this.search$.pipe(
distinctUntilChanged(), distinctUntilChanged(),
skip(1) skip(1)
).subscribe( ).subscribe(
(search) => { (search) => {
this.widgetsBundleFilter = search; this.widgetsBundleFilter = search;
this.iotHubWidgetsFilter = search;
this.cd.markForCheck(); this.cd.markForCheck();
} }
); );
@ -252,10 +272,64 @@ export class DashboardWidgetSelectComponent implements OnInit {
} }
} }
onIotHubWidgetClicked($event: Event, item: MpItemVersionView): void {
$event.preventDefault();
const dialogRef = this.dialog.open(TbIotHubItemDetailDialogComponent, {
panelClass: ['tb-dialog', 'tb-fullscreen-dialog'],
autoFocus: false,
data: {
item,
iotHubApiService: this.iotHubApiService,
mode: 'add'
} as IotHubItemDetailDialogData
});
dialogRef.afterClosed().subscribe(result => {
if (result?.action === 'add') {
this.installAndAddWidget(result.item);
}
});
}
getIotHubItemImage(item: MpItemVersionView): string | null {
if (item.image) {
return this.iotHubApiService.resolveResourceUrl(item.image);
}
const resource = item.resources?.find(r => r.type === 'SCREENSHOT') || item.resources?.find(r => r.type === 'ICON');
if (resource) {
return this.iotHubApiService.resolveResourceUrl(`/api/resources/${resource.id}`);
}
return null;
}
isObject(value: any): boolean { isObject(value: any): boolean {
return isObject(value); return isObject(value);
} }
private installAndAddWidget(item: MpItemVersionView): void {
const versionId = item.id as string;
this.iotHubApiService.installItemVersion(versionId, { ignoreLoading: true }).subscribe({
next: (result) => {
if (result.success && result.descriptor?.type === 'WIDGET') {
const widgetTypeId = result.descriptor.widgetTypeId?.id;
if (widgetTypeId) {
this.widgetsService.getWidgetTypeInfoById(widgetTypeId).subscribe(widgetType => {
if (widgetType) {
this.widgetSelected.emit({
typeFullFqn: fullWidgetTypeFqn(widgetType),
type: widgetType.widgetType,
title: widgetType.name,
image: widgetType.image,
description: widgetType.description,
deprecated: widgetType.deprecated
});
}
});
}
}
}
});
}
private toWidgetInfo(widgetTypeInfo: WidgetTypeInfo): WidgetInfo { private toWidgetInfo(widgetTypeInfo: WidgetTypeInfo): WidgetInfo {
return { return {
typeFullFqn: fullWidgetTypeFqn(widgetTypeInfo), typeFullFqn: fullWidgetTypeFqn(widgetTypeInfo),

3
ui-ngx/src/app/modules/home/components/home-components.module.ts

@ -112,6 +112,7 @@ import { StatesControllerModule } from '@home/components/dashboard-page/states/s
import { DashboardLayoutComponent } from '@home/components/dashboard-page/layout/dashboard-layout.component'; import { DashboardLayoutComponent } from '@home/components/dashboard-page/layout/dashboard-layout.component';
import { EditWidgetComponent } from '@home/components/dashboard-page/edit-widget.component'; import { EditWidgetComponent } from '@home/components/dashboard-page/edit-widget.component';
import { DashboardWidgetSelectComponent } from '@home/components/dashboard-page/dashboard-widget-select.component'; import { DashboardWidgetSelectComponent } from '@home/components/dashboard-page/dashboard-widget-select.component';
import { TbIotHubItemDetailDialogComponent } from '@home/components/iot-hub/iot-hub-item-detail-dialog.component';
import { AddWidgetDialogComponent } from '@home/components/dashboard-page/add-widget-dialog.component'; import { AddWidgetDialogComponent } from '@home/components/dashboard-page/add-widget-dialog.component';
import { ManageDashboardLayoutsDialogComponent } from '@home/components/dashboard-page/layout/manage-dashboard-layouts-dialog.component'; import { ManageDashboardLayoutsDialogComponent } from '@home/components/dashboard-page/layout/manage-dashboard-layouts-dialog.component';
import { import {
@ -321,6 +322,7 @@ import { SolutionInstallDialogComponent } from '@home/components/solution/soluti
SelectDashboardBreakpointComponent, SelectDashboardBreakpointComponent,
EditWidgetComponent, EditWidgetComponent,
DashboardWidgetSelectComponent, DashboardWidgetSelectComponent,
TbIotHubItemDetailDialogComponent,
AddWidgetDialogComponent, AddWidgetDialogComponent,
MoveWidgetsDialogComponent, MoveWidgetsDialogComponent,
ManageDashboardLayoutsDialogComponent, ManageDashboardLayoutsDialogComponent,
@ -471,6 +473,7 @@ import { SolutionInstallDialogComponent } from '@home/components/solution/soluti
SelectDashboardBreakpointComponent, SelectDashboardBreakpointComponent,
EditWidgetComponent, EditWidgetComponent,
DashboardWidgetSelectComponent, DashboardWidgetSelectComponent,
TbIotHubItemDetailDialogComponent,
AddWidgetDialogComponent, AddWidgetDialogComponent,
MoveWidgetsDialogComponent, MoveWidgetsDialogComponent,
ManageDashboardLayoutsDialogComponent, ManageDashboardLayoutsDialogComponent,

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

@ -79,20 +79,26 @@
</div> </div>
</div> </div>
<div class="dlg-info-actions"> <div class="dlg-info-actions">
@if (!isInstalled()) { @if (mode === 'add') {
<button mat-flat-button color="primary" (click)="install()"> <button mat-flat-button color="primary" (click)="addItem()">
{{ 'iot-hub.install' | translate }} {{ 'action.add' | translate }}
</button>
} @else if (hasUpdate()) {
<button mat-flat-button color="primary" (click)="updateItem()">
{{ 'iot-hub.update' | translate }}
</button>
}
@if (isInstalled()) {
<button mat-stroked-button color="warn" (click)="deleteItem()">
<mat-icon>delete</mat-icon>
{{ 'iot-hub.remove' | translate }}
</button> </button>
} @else {
@if (!isInstalled()) {
<button mat-flat-button color="primary" (click)="install()">
{{ 'iot-hub.install' | translate }}
</button>
} @else if (hasUpdate()) {
<button mat-flat-button color="primary" (click)="updateItem()">
{{ 'iot-hub.update' | translate }}
</button>
}
@if (isInstalled()) {
<button mat-stroked-button color="warn" (click)="deleteItem()">
<mat-icon>delete</mat-icon>
{{ 'iot-hub.remove' | translate }}
</button>
}
} }
</div> </div>
</div> </div>
@ -192,20 +198,26 @@
</div> </div>
<!-- Action buttons --> <!-- Action buttons -->
<div class="dlg-info-actions"> <div class="dlg-info-actions">
@if (!isInstalled()) { @if (mode === 'add') {
<button mat-flat-button color="primary" (click)="install()"> <button mat-flat-button color="primary" (click)="addItem()">
{{ 'iot-hub.install' | translate }} {{ 'action.add' | translate }}
</button>
} @else if (hasUpdate()) {
<button mat-flat-button color="primary" (click)="updateItem()">
{{ 'iot-hub.update' | translate }}
</button>
}
@if (isInstalled()) {
<button mat-stroked-button color="warn" (click)="deleteItem()">
<tb-icon matButtonIcon class="tb-mat-24">mdi:trash-can-outline</tb-icon>
{{ 'iot-hub.remove' | translate }}
</button> </button>
} @else {
@if (!isInstalled()) {
<button mat-flat-button color="primary" (click)="install()">
{{ 'iot-hub.install' | translate }}
</button>
} @else if (hasUpdate()) {
<button mat-flat-button color="primary" (click)="updateItem()">
{{ 'iot-hub.update' | translate }}
</button>
}
@if (isInstalled()) {
<button mat-stroked-button color="warn" (click)="deleteItem()">
<tb-icon matButtonIcon class="tb-mat-24">mdi:trash-can-outline</tb-icon>
{{ 'iot-hub.remove' | translate }}
</button>
}
} }
</div> </div>
</div> </div>

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

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

@ -24,17 +24,20 @@ import { IotHubApiService } from '@core/http/iot-hub-api.service';
import { TranslateService } from '@ngx-translate/core'; import { TranslateService } from '@ngx-translate/core';
import { EntityType } from '@shared/models/entity-type.models'; import { EntityType } from '@shared/models/entity-type.models';
import { getEntityDetailsPageURL } from '@core/utils'; import { getEntityDetailsPageURL } from '@core/utils';
import { TbIotHubInstallDialogComponent, IotHubInstallDialogData } from './iot-hub-install-dialog.component'; import { TbIotHubInstallDialogComponent, IotHubInstallDialogData } from '@home/pages/iot-hub/iot-hub-install-dialog.component';
import { TbIotHubUpdateDialogComponent, IotHubUpdateDialogData } from './iot-hub-update-dialog.component'; import { TbIotHubUpdateDialogComponent, IotHubUpdateDialogData } from '@home/pages/iot-hub/iot-hub-update-dialog.component';
import { TbIotHubDeleteDialogComponent, IotHubDeleteDialogData } from './iot-hub-delete-dialog.component'; import { TbIotHubDeleteDialogComponent, IotHubDeleteDialogData } from '@home/pages/iot-hub/iot-hub-delete-dialog.component';
import { TbDeviceInstallDialogComponent, DeviceInstallDialogData } from './device-install-dialog/device-install-dialog.component'; import { TbDeviceInstallDialogComponent, DeviceInstallDialogData } from '@home/pages/iot-hub/device-install-dialog/device-install-dialog.component';
import { SolutionInstallDialogComponent } from '@home/components/solution/solution-install-dialog.component'; import { SolutionInstallDialogComponent } from '@home/components/solution/solution-install-dialog.component';
import { SolutionTemplateInstalledItemDescriptor } from '@shared/models/iot-hub/iot-hub-installed-item.models'; import { SolutionTemplateInstalledItemDescriptor } from '@shared/models/iot-hub/iot-hub-installed-item.models';
export type IotHubItemDetailDialogMode = 'default' | 'add';
export interface IotHubItemDetailDialogData { export interface IotHubItemDetailDialogData {
item: MpItemVersionView; item: MpItemVersionView;
iotHubApiService: IotHubApiService; iotHubApiService: IotHubApiService;
installedItem?: IotHubInstalledItem; installedItem?: IotHubInstalledItem;
mode?: IotHubItemDetailDialogMode;
} }
@Component({ @Component({
@ -47,6 +50,7 @@ export class TbIotHubItemDetailDialogComponent {
readonly ItemType = ItemType; readonly ItemType = ItemType;
item: MpItemVersionView; item: MpItemVersionView;
mode: IotHubItemDetailDialogMode;
typeTranslations = itemTypeTranslations; typeTranslations = itemTypeTranslations;
readmeContent: string = ''; readmeContent: string = '';
installedItem?: IotHubInstalledItem; installedItem?: IotHubInstalledItem;
@ -64,6 +68,7 @@ export class TbIotHubItemDetailDialogComponent {
private translate: TranslateService private translate: TranslateService
) { ) {
this.item = data.item; this.item = data.item;
this.mode = data.mode || 'default';
this.installedItem = data.installedItem; this.installedItem = data.installedItem;
this.categoryMap = getCategoriesForType(this.item.type); this.categoryMap = getCategoriesForType(this.item.type);
this.buildCarouselImages(); this.buildCarouselImages();
@ -368,6 +373,10 @@ export class TbIotHubItemDetailDialogComponent {
}); });
} }
addItem(): void {
this.dialogRef.close({ action: 'add', item: this.item });
}
close(): void { close(): void {
this.dialogRef.close(); this.dialogRef.close();
} }

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

@ -31,7 +31,7 @@ import { IotHubApiService } from '@core/http/iot-hub-api.service';
import { MatDialog } from '@angular/material/dialog'; import { MatDialog } from '@angular/material/dialog';
import { TranslateService } from '@ngx-translate/core'; import { TranslateService } from '@ngx-translate/core';
import { ActivatedRoute, Router } from '@angular/router'; import { ActivatedRoute, Router } from '@angular/router';
import { TbIotHubItemDetailDialogComponent, IotHubItemDetailDialogData } from './iot-hub-item-detail-dialog.component'; import { TbIotHubItemDetailDialogComponent, IotHubItemDetailDialogData } from '@home/components/iot-hub/iot-hub-item-detail-dialog.component';
import { TbIotHubInstallDialogComponent, IotHubInstallDialogData } from './iot-hub-install-dialog.component'; import { TbIotHubInstallDialogComponent, IotHubInstallDialogData } from './iot-hub-install-dialog.component';
import { TbIotHubUpdateDialogComponent, IotHubUpdateDialogData } from './iot-hub-update-dialog.component'; import { TbIotHubUpdateDialogComponent, IotHubUpdateDialogData } from './iot-hub-update-dialog.component';
import { TbIotHubDeleteDialogComponent, IotHubDeleteDialogData } from './iot-hub-delete-dialog.component'; import { TbIotHubDeleteDialogComponent, IotHubDeleteDialogData } from './iot-hub-delete-dialog.component';

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

@ -54,7 +54,8 @@
[class.active]="activeHeroType === ht" [class.active]="activeHeroType === ht"
[style.--keyword-color]="ht.color" [style.--keyword-color]="ht.color"
(mouseenter)="onHeroTypeHover(ht)" (mouseenter)="onHeroTypeHover(ht)"
(mouseleave)="onHeroTypeLeave()">{{ ht.labelKey | translate }}@if (!last) {,}</span> (mouseleave)="onHeroTypeLeave()"
(click)="navigateToBrowse(ht.type)">{{ ht.labelKey | translate }}@if (!last) {,}</span>
@if (i === 2) { @if (i === 2) {
<span class="tb-iot-hub-hero-subtitle-break"></span> <span class="tb-iot-hub-hero-subtitle-break"></span>
} }

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

@ -30,7 +30,7 @@ import { ItemType, itemTypeTranslations } from '@shared/models/iot-hub/iot-hub-i
import { IotHubInstalledItem } from '@shared/models/iot-hub/iot-hub-installed-item.models'; import { IotHubInstalledItem } from '@shared/models/iot-hub/iot-hub-installed-item.models';
import { IotHubApiService } from '@core/http/iot-hub-api.service'; import { IotHubApiService } from '@core/http/iot-hub-api.service';
import { TranslateService } from '@ngx-translate/core'; import { TranslateService } from '@ngx-translate/core';
import { TbIotHubItemDetailDialogComponent, IotHubItemDetailDialogData } from './iot-hub-item-detail-dialog.component'; import { TbIotHubItemDetailDialogComponent, IotHubItemDetailDialogData } from '@home/components/iot-hub/iot-hub-item-detail-dialog.component';
import { TbIotHubInstallDialogComponent, IotHubInstallDialogData } from './iot-hub-install-dialog.component'; import { TbIotHubInstallDialogComponent, IotHubInstallDialogData } from './iot-hub-install-dialog.component';
import { TbIotHubUpdateDialogComponent, IotHubUpdateDialogData } from './iot-hub-update-dialog.component'; import { TbIotHubUpdateDialogComponent, IotHubUpdateDialogData } from './iot-hub-update-dialog.component';
import { TbIotHubDeleteDialogComponent, IotHubDeleteDialogData } from './iot-hub-delete-dialog.component'; import { TbIotHubDeleteDialogComponent, IotHubDeleteDialogData } from './iot-hub-delete-dialog.component';

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

@ -33,7 +33,7 @@ import { IotHubInstalledItem, ItemPublishedVersionInfo } from '@shared/models/io
import { ItemType, itemTypeTranslations } from '@shared/models/iot-hub/iot-hub-item.models'; import { ItemType, itemTypeTranslations } from '@shared/models/iot-hub/iot-hub-item.models';
import { EntityType } from '@shared/models/entity-type.models'; import { EntityType } from '@shared/models/entity-type.models';
import { getEntityDetailsPageURL } from '@core/utils'; import { getEntityDetailsPageURL } from '@core/utils';
import { TbIotHubItemDetailDialogComponent, IotHubItemDetailDialogData } from './iot-hub-item-detail-dialog.component'; import { TbIotHubItemDetailDialogComponent, IotHubItemDetailDialogData } from '@home/components/iot-hub/iot-hub-item-detail-dialog.component';
import { TbIotHubUpdateDialogComponent, IotHubUpdateDialogData } from './iot-hub-update-dialog.component'; import { TbIotHubUpdateDialogComponent, IotHubUpdateDialogData } from './iot-hub-update-dialog.component';
import { TbIotHubDeleteDialogComponent, IotHubDeleteDialogData } from './iot-hub-delete-dialog.component'; import { TbIotHubDeleteDialogComponent, IotHubDeleteDialogData } from './iot-hub-delete-dialog.component';

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

@ -26,7 +26,7 @@ import { MpItemVersionQuery, MpItemVersionView } from '@shared/models/iot-hub/io
import { ItemType, itemTypeTranslations } from '@shared/models/iot-hub/iot-hub-item.models'; import { ItemType, itemTypeTranslations } from '@shared/models/iot-hub/iot-hub-item.models';
import { IotHubInstalledItem } from '@shared/models/iot-hub/iot-hub-installed-item.models'; import { IotHubInstalledItem } from '@shared/models/iot-hub/iot-hub-installed-item.models';
import { IotHubApiService } from '@core/http/iot-hub-api.service'; import { IotHubApiService } from '@core/http/iot-hub-api.service';
import { TbIotHubItemDetailDialogComponent, IotHubItemDetailDialogData } from './iot-hub-item-detail-dialog.component'; import { TbIotHubItemDetailDialogComponent, IotHubItemDetailDialogData } from '@home/components/iot-hub/iot-hub-item-detail-dialog.component';
import { TbIotHubInstallDialogComponent, IotHubInstallDialogData } from './iot-hub-install-dialog.component'; import { TbIotHubInstallDialogComponent, IotHubInstallDialogData } from './iot-hub-install-dialog.component';
import { TbIotHubUpdateDialogComponent, IotHubUpdateDialogData } from './iot-hub-update-dialog.component'; import { TbIotHubUpdateDialogComponent, IotHubUpdateDialogData } from './iot-hub-update-dialog.component';
import { TbIotHubDeleteDialogComponent, IotHubDeleteDialogData } from './iot-hub-delete-dialog.component'; import { TbIotHubDeleteDialogComponent, IotHubDeleteDialogData } from './iot-hub-delete-dialog.component';

2
ui-ngx/src/app/modules/home/pages/iot-hub/iot-hub.module.ts

@ -22,7 +22,6 @@ import { TbIotHubHomeComponent } from './iot-hub-home.component';
import { TbIotHubItemsPageComponent } from './iot-hub-items-page.component'; import { TbIotHubItemsPageComponent } from './iot-hub-items-page.component';
import { TbIotHubBrowseComponent } from './iot-hub-browse.component'; import { TbIotHubBrowseComponent } from './iot-hub-browse.component';
import { TbIotHubCreatorProfileComponent } from './iot-hub-creator-profile.component'; import { TbIotHubCreatorProfileComponent } from './iot-hub-creator-profile.component';
import { TbIotHubItemDetailDialogComponent } from './iot-hub-item-detail-dialog.component';
import { TbIotHubInstallDialogComponent } from './iot-hub-install-dialog.component'; import { TbIotHubInstallDialogComponent } from './iot-hub-install-dialog.component';
import { TbIotHubItemCardComponent } from './iot-hub-item-card.component'; import { TbIotHubItemCardComponent } from './iot-hub-item-card.component';
import { TbIotHubInstalledItemsComponent } from './iot-hub-installed-items.component'; import { TbIotHubInstalledItemsComponent } from './iot-hub-installed-items.component';
@ -37,7 +36,6 @@ import { TbIotHubSearchComponent } from './iot-hub-search.component';
TbIotHubItemsPageComponent, TbIotHubItemsPageComponent,
TbIotHubBrowseComponent, TbIotHubBrowseComponent,
TbIotHubCreatorProfileComponent, TbIotHubCreatorProfileComponent,
TbIotHubItemDetailDialogComponent,
TbIotHubInstallDialogComponent, TbIotHubInstallDialogComponent,
TbIotHubItemCardComponent, TbIotHubItemCardComponent,
TbIotHubInstalledItemsComponent, TbIotHubInstalledItemsComponent,

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

@ -3710,6 +3710,7 @@
"search-results": "Search results", "search-results": "Search results",
"search-results-for": "Search results for ", "search-results-for": "Search results for ",
"try-adjusting-search": "Try adjusting your search", "try-adjusting-search": "Try adjusting your search",
"add-widget-from-iot-hub": "Add widget from IoT Hub",
"items-per-page": "Items per page", "items-per-page": "Items per page",
"filter": "Filter", "filter": "Filter",
"available": "Available", "available": "Available",

Loading…
Cancel
Save