Browse Source

Redesign installed items page to match design

- Back link + "Installed Items" title + "Check for updates" primary button
- Filter button (stroked primary) with dropdown checkbox panel for item types
- Filter chips (mat-chip-set) with "Clear all" button when active
- Search field matching browse page design (320px, rounded-8, outlined)
- Mat-table with columns: Name, Type (colored icon+text), Version,
  Installed date, Updates (up-to-date/available+update btn), Actions
- Actions: info, open-in-new, remove (mdi:trash-can-outline) icon buttons
- Updates column: green "Up to date" or "vX.X.X Available" + Update stroked warn
- Item type filter passed as List<String> to API
- Use TbIotHubDeleteDialogComponent for remove confirmation
pull/15508/head
Igor Kulikov 6 months ago
parent
commit
00930b1f77
  1. 200
      ui-ngx/src/app/modules/home/pages/iot-hub/iot-hub-installed-items.component.html
  2. 351
      ui-ngx/src/app/modules/home/pages/iot-hub/iot-hub-installed-items.component.scss
  3. 69
      ui-ngx/src/app/modules/home/pages/iot-hub/iot-hub-installed-items.component.ts
  4. 2
      ui-ngx/src/assets/locale/locale.constant-en_US.json

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

@ -15,141 +15,171 @@
limitations under the License. limitations under the License.
--> -->
<div class="tb-iot-hub-installed-items-container"> <div class="tb-installed-items-page">
<a class="tb-iot-hub-back-link" (click)="navigateToMarketplace()"> <!-- Back link -->
<mat-icon>arrow_back</mat-icon> <button mat-button color="primary" class="tb-installed-back-btn" (click)="navigateToMarketplace()">
{{ 'iot-hub.back-to-marketplace' | translate }} <mat-icon>chevron_left</mat-icon>
</a> {{ 'iot-hub.back-to-iot-hub' | translate }}
<div class="tb-iot-hub-installed-items-header"> </button>
<h2>{{ 'iot-hub.installed-items' | translate }}</h2>
@if (!updatesChecked) { <!-- Header -->
<button mat-flat-button color="primary" <div class="tb-installed-header">
[disabled]="isCheckingUpdates" <h1 class="tb-installed-title">{{ 'iot-hub.installed-items' | translate }}</h1>
(click)="checkForUpdates()"> <button mat-flat-button color="primary" (click)="checkForUpdates()" [disabled]="isCheckingUpdates">
@if (isCheckingUpdates) { @if (isCheckingUpdates) {
<mat-spinner diameter="18" class="tb-iot-hub-inline-spinner"></mat-spinner> <mat-spinner diameter="18"></mat-spinner>
} @else { } @else {
<mat-icon>update</mat-icon> <mat-icon>sync</mat-icon>
} }
{{ 'iot-hub.check-for-updates' | translate }} {{ 'iot-hub.check-for-updates' | translate }}
</button> </button>
}
</div> </div>
<div class="tb-iot-hub-installed-search-row"> <!-- Filter + Search row -->
<mat-form-field appearance="outline" class="tb-iot-hub-installed-search-field"> <div class="tb-installed-toolbar">
<mat-icon matPrefix>search</mat-icon> <div class="tb-installed-filter-area">
<input matInput <button mat-stroked-button color="primary" class="tb-installed-filter-btn" (click)="toggleFilter()">
[placeholder]="'iot-hub.search-installed-items' | translate" <mat-icon>filter_list</mat-icon>
[value]="textSearch" {{ 'iot-hub.filter' | translate }}
(input)="onSearchChange($any($event.target).value)"> </button>
@if (textSearch) { @if (hasActiveFilters()) {
<button matSuffix mat-icon-button (click)="onSearchChange('')"> <mat-chip-set class="tb-installed-filter-chips">
<mat-icon>close</mat-icon> @for (type of activeTypeFilters; track type) {
<mat-chip (removed)="removeTypeFilter(type)">
{{ getItemTypeLabel(type) }}
<mat-icon matChipRemove>close</mat-icon>
</mat-chip>
}
</mat-chip-set>
<button class="tb-installed-clear-btn" (click)="clearAllFilters()">
{{ 'iot-hub.clear-all' | translate }}
</button> </button>
} }
</mat-form-field> </div>
<div class="tb-installed-search">
<mat-icon class="tb-installed-search-icon">search</mat-icon>
<input class="tb-installed-search-input"
[placeholder]="'iot-hub.search' | translate"
[value]="textSearch"
(input)="onSearchChange($any($event.target).value)">
</div>
</div> </div>
<div class="tb-iot-hub-installed-table-container"> <!-- Filter dropdown -->
@if (isLoading) { @if (filterOpen) {
<mat-progress-bar mode="indeterminate"></mat-progress-bar> <div class="tb-installed-filter-dropdown">
} @for (type of allItemTypes; track type) {
<mat-checkbox [checked]="isTypeFilterActive(type)" (change)="toggleTypeFilter(type)">
{{ getItemTypeLabel(type) }}
</mat-checkbox>
}
</div>
}
<table mat-table [dataSource]="dataSource" matSort> <!-- Table -->
<div class="tb-installed-table-container">
<table mat-table [dataSource]="dataSource" matSort matSortActive="createdTime" matSortDirection="desc">
<!-- Name -->
<ng-container matColumnDef="itemName"> <ng-container matColumnDef="itemName">
<th mat-header-cell *matHeaderCellDef mat-sort-header>{{ 'iot-hub.item-name' | translate }}</th> <th mat-header-cell *matHeaderCellDef mat-sort-header>{{ 'iot-hub.item-name' | translate }}</th>
<td mat-cell *matCellDef="let item">{{ item.itemName }}</td> <td mat-cell *matCellDef="let item">{{ item.itemName }}</td>
</ng-container> </ng-container>
<!-- Type -->
<ng-container matColumnDef="itemType"> <ng-container matColumnDef="itemType">
<th mat-header-cell *matHeaderCellDef mat-sort-header>{{ 'iot-hub.item-type' | translate }}</th> <th mat-header-cell *matHeaderCellDef>{{ 'iot-hub.item-type' | translate }}</th>
<td mat-cell *matCellDef="let item"> <td mat-cell *matCellDef="let item">
<span class="tb-iot-hub-type-chip" [ngClass]="getItemTypeChipClass(item.itemType)">{{ getItemTypeLabel(item.itemType) }}</span> <span class="tb-installed-type" [ngClass]="getItemTypeChipClass(item.itemType)">
<mat-icon class="tb-installed-type-icon">{{ getItemTypeIcon(item.itemType) }}</mat-icon>
{{ getItemTypeLabel(item.itemType) }}
</span>
</td> </td>
</ng-container> </ng-container>
<!-- Version -->
<ng-container matColumnDef="version"> <ng-container matColumnDef="version">
<th mat-header-cell *matHeaderCellDef mat-sort-header>{{ 'iot-hub.item-version' | translate }}</th> <th mat-header-cell *matHeaderCellDef>{{ 'iot-hub.item-version' | translate }}</th>
<td mat-cell *matCellDef="let item">{{ item.version }}</td> <td mat-cell *matCellDef="let item">{{ item.version }}</td>
</ng-container> </ng-container>
<!-- Installed date -->
<ng-container matColumnDef="createdTime"> <ng-container matColumnDef="createdTime">
<th mat-header-cell *matHeaderCellDef mat-sort-header>{{ 'iot-hub.installed-date' | translate }}</th> <th mat-header-cell *matHeaderCellDef mat-sort-header>{{ 'iot-hub.installed-date' | translate }}</th>
<td mat-cell *matCellDef="let item">{{ item.createdTime | date:'mediumDate' }}</td> <td mat-cell *matCellDef="let item">{{ item.createdTime | date:'mediumDate' }}</td>
</ng-container> </ng-container>
<!-- Updates -->
<ng-container matColumnDef="updates"> <ng-container matColumnDef="updates">
<th mat-header-cell *matHeaderCellDef>{{ updatesChecked ? ('iot-hub.updates' | translate) : '' }}</th> <th mat-header-cell *matHeaderCellDef>{{ 'iot-hub.updates' | translate }}</th>
<td mat-cell *matCellDef="let item"> <td mat-cell *matCellDef="let item">
@if (updatesChecked) { @if (updatesChecked) {
@if (getPublishedVersionInfo(item); as publishedInfo) { @if (getPublishedVersionInfo(item); as pubInfo) {
@if (publishedInfo.publishedVersionId !== item.itemVersionId) { @if (pubInfo.publishedVersionId !== item.itemVersionId) {
<button mat-stroked-button class="tb-iot-hub-update-button" <span class="tb-installed-update-available">
[matTooltip]="'iot-hub.view-item-details' | translate" <mat-icon>arrow_upward</mat-icon>
matTooltipPosition="above" v{{ pubInfo.publishedVersion }} {{ 'iot-hub.available' | translate }}
(click)="viewUpdateDetails(publishedInfo, item)"> </span>
<mat-icon>info_outline</mat-icon> <button mat-stroked-button color="warn" class="tb-installed-update-btn" (click)="updateItem(item, pubInfo)">
{{ publishedInfo.publishedVersion }}
</button>
<button mat-stroked-button color="primary" class="tb-iot-hub-update-button"
(click)="updateItem(item, publishedInfo)">
{{ 'iot-hub.update' | translate }} {{ 'iot-hub.update' | translate }}
</button> </button>
} @else { } @else {
<span class="tb-iot-hub-up-to-date">{{ 'iot-hub.up-to-date' | translate }}</span> <span class="tb-installed-up-to-date">
<mat-icon>check</mat-icon>
{{ 'iot-hub.up-to-date' | translate }}
</span>
} }
} @else { } @else {
<span class="tb-iot-hub-up-to-date">{{ 'iot-hub.up-to-date' | translate }}</span> <span class="tb-installed-up-to-date">
<mat-icon>check</mat-icon>
{{ 'iot-hub.up-to-date' | translate }}
</span>
} }
} }
</td> </td>
</ng-container> </ng-container>
<!-- Actions -->
<ng-container matColumnDef="actions"> <ng-container matColumnDef="actions">
<th mat-header-cell *matHeaderCellDef></th> <th mat-header-cell *matHeaderCellDef></th>
<td mat-cell *matCellDef="let item"> <td mat-cell *matCellDef="let item">
<button mat-icon-button <div class="tb-installed-actions">
[matTooltip]="'iot-hub.view-item-details' | translate" <button mat-icon-button (click)="viewItemDetails(item)" matTooltip="{{ 'iot-hub.view-item-details' | translate }}">
matTooltipPosition="above" <mat-icon>info_outline</mat-icon>
(click)="viewItemDetails(item); $event.stopPropagation()"> </button>
<mat-icon>info_outline</mat-icon> <button mat-icon-button (click)="openEntity(item)" matTooltip="{{ 'iot-hub.open-installed-entity' | translate }}">
</button>
@if (getEntityId(item)) {
<button mat-icon-button color="primary"
[matTooltip]="'iot-hub.open-installed-entity' | translate"
matTooltipPosition="above"
(click)="openEntity(item); $event.stopPropagation()">
<mat-icon>open_in_new</mat-icon> <mat-icon>open_in_new</mat-icon>
</button> </button>
} <button mat-icon-button (click)="deleteItem(item)" matTooltip="{{ 'iot-hub.remove' | translate }}">
<button mat-icon-button color="warn" <tb-icon>mdi:trash-can-outline</tb-icon>
[matTooltip]="'action.delete' | translate" </button>
matTooltipPosition="above" </div>
(click)="deleteItem(item); $event.stopPropagation()">
<mat-icon>delete</mat-icon>
</button>
</td> </td>
</ng-container> </ng-container>
<tr mat-header-row *matHeaderRowDef="displayedColumns"></tr> <tr mat-header-row *matHeaderRowDef="displayedColumns"></tr>
<tr mat-row *matRowDef="let row; columns: displayedColumns;"></tr> <tr mat-row *matRowDef="let row; columns: displayedColumns;"></tr>
<tr class="mat-row" *matNoDataRow>
<td class="mat-cell" [attr.colspan]="displayedColumns.length">
<div class="tb-iot-hub-installed-empty">
{{ 'iot-hub.no-installed-items' | translate }}
</div>
</td>
</tr>
</table> </table>
<mat-paginator @if (isLoading) {
[length]="totalElements" <div class="tb-installed-loading">
[pageSize]="pageSize" <mat-spinner diameter="40"></mat-spinner>
[pageIndex]="pageIndex" </div>
[pageSizeOptions]="[10, 20, 50]" }
(page)="onPageChange($event)">
</mat-paginator> @if (!isLoading && dataSource.length === 0) {
<div class="tb-installed-empty">
{{ 'iot-hub.no-installed-items' | translate }}
</div>
}
</div> </div>
<!-- Paginator -->
<mat-paginator
[length]="totalElements"
[pageSize]="pageSize"
[pageIndex]="pageIndex"
[pageSizeOptions]="[10, 25, 50]"
(page)="onPageChange($event)"
showFirstLastButtons>
</mat-paginator>
</div> </div>

351
ui-ngx/src/app/modules/home/pages/iot-hub/iot-hub-installed-items.component.scss

@ -14,246 +14,233 @@
* limitations under the License. * limitations under the License.
*/ */
@import "../../../../../scss/constants";
:host { :host {
display: flex; display: block;
flex-direction: column; padding: 16px;
height: 100%;
padding: 16px 24px;
overflow: auto;
} }
.tb-iot-hub-installed-items-container { .tb-installed-items-page {
max-width: 1400px;
margin: 0 auto;
width: 100%;
background: white; background: white;
padding: 24px; border-radius: 8px;
border-radius: 4px; padding: 40px 40px 24px;
box-shadow: 0 0 1px rgba(0, 0, 0, 0.14), 0 1px 1px rgba(0, 0, 0, 0.12);
} }
.tb-iot-hub-back-link { // Back button
display: inline-flex; .tb-installed-back-btn {
align-items: center; margin-bottom: 40px;
gap: 4px;
font-size: 13px;
color: rgba(0, 0, 0, 0.54);
cursor: pointer;
text-decoration: none;
border-bottom: none;
margin-bottom: 12px;
mat-icon {
font-size: 18px;
width: 18px;
height: 18px;
}
&:hover {
color: rgba(0, 0, 0, 0.87);
text-decoration: none;
border-bottom: none;
}
} }
.tb-iot-hub-installed-items-header { // Header: title + check for updates
.tb-installed-header {
display: flex; display: flex;
align-items: center; align-items: center;
justify-content: space-between; justify-content: space-between;
margin-bottom: 24px; margin-bottom: 32px;
h2 {
font-size: 28px;
font-weight: 700;
margin: 0;
color: rgba(0, 0, 0, 0.87);
}
} }
.tb-iot-hub-inline-spinner { .tb-installed-title {
display: inline-block; font-size: 40px;
margin-right: 8px; font-weight: 500;
line-height: 48px;
color: rgba(0, 0, 0, 0.87);
margin: 0;
} }
.tb-iot-hub-installed-search-row { // Toolbar: filter + search
.tb-installed-toolbar {
display: flex; display: flex;
gap: 12px;
margin-bottom: 16px;
align-items: center; align-items: center;
justify-content: space-between;
margin-bottom: 16px;
} }
.tb-iot-hub-installed-search-field { .tb-installed-filter-area {
flex: 1; display: flex;
align-items: center;
::ng-deep { gap: 8px;
.mat-mdc-text-field-wrapper { }
height: 42px;
padding: 0 12px;
}
.mat-mdc-form-field-flex {
height: 42px;
align-items: center;
}
.mdc-notched-outline__leading,
.mdc-notched-outline__notch,
.mdc-notched-outline__trailing {
border-color: #d5dbe3;
}
.mdc-notched-outline__leading {
border-radius: 8px 0 0 8px;
}
.mdc-notched-outline__trailing {
border-radius: 0 8px 8px 0;
}
.mat-mdc-form-field-infix {
padding-top: 0;
padding-bottom: 0;
min-height: unset;
}
.mdc-notched-outline__notch { .tb-installed-filter-btn {
display: none; &.mat-primary {
} --mat-button-outlined-outline-color: #{$tb-primary-color};
}
}
.mat-mdc-floating-label { .tb-installed-filter-chips {
display: none; .mat-mdc-standard-chip:not(.mdc-evolution-chip--disabled) {
} --mat-chip-elevated-container-color: rgba(0, 0, 0, 0.04);
}
}
.mat-mdc-form-field-subscript-wrapper { .tb-installed-clear-btn {
display: none; font-size: 14px;
} font-weight: 400;
color: rgba(0, 0, 0, 0.54);
cursor: pointer;
background: none;
border: none;
padding: 6px 8px;
font-family: inherit;
input.mat-mdc-input-element { &:hover {
font-size: 14px; color: rgba(0, 0, 0, 0.87);
color: #3e4a59; }
}
&::placeholder { // Search field
color: #8b95a2; .tb-installed-search {
opacity: 1; width: 320px;
} height: 40px;
} display: flex;
align-items: center;
gap: 8px;
padding: 8px 12px;
border-radius: 8px;
border: 1px solid rgba(0, 0, 0, 0.12);
background: white;
}
.mat-icon { .tb-installed-search-icon {
color: #8b95a2; font-size: 24px;
font-size: 20px; width: 24px;
width: 20px; height: 24px;
height: 20px; color: rgba(0, 0, 0, 0.38);
} flex-shrink: 0;
}
.mat-mdc-form-field-icon-prefix { .tb-installed-search-input {
padding: 0 8px 0 0; flex: 1;
border: none;
outline: none;
font-size: 14px;
font-weight: 400;
color: rgba(0, 0, 0, 0.87);
background: transparent;
font-family: inherit;
> .mat-icon { &::placeholder {
padding: 0; color: rgba(0, 0, 0, 0.38);
} }
} }
.mat-mdc-form-field-icon-suffix { // Filter dropdown
padding: 0; .tb-installed-filter-dropdown {
display: flex;
flex-direction: column;
width: 260px;
padding: 8px 0;
background: white;
border: 1px solid rgba(0, 0, 0, 0.12);
border-radius: 4px;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
margin-bottom: 16px;
.mat-mdc-icon-button { mat-checkbox {
width: 32px; display: block;
height: 32px; padding: 8px 16px;
padding: 4px;
}
}
}
&.mat-focused ::ng-deep { &:hover {
.mdc-notched-outline__leading, background: rgba(0, 0, 0, 0.04);
.mdc-notched-outline__notch,
.mdc-notched-outline__trailing {
border-color: #8b95a2;
border-width: 1px;
} }
} }
} }
.tb-iot-hub-installed-table-container { // Table
.tb-installed-table-container {
border: 1px solid rgba(0, 0, 0, 0.12);
border-radius: 4px; border-radius: 4px;
overflow: hidden; overflow: hidden;
table { table {
width: 100%; width: 100%;
} }
}
.mat-column-itemName { // Type cell with colored icon + text
width: 100%; .tb-installed-type {
} display: inline-flex;
align-items: center;
.mat-column-itemType { gap: 4px;
width: 170px; font-size: 14px;
min-width: 170px; font-weight: 500;
max-width: 170px; }
}
.mat-column-version { .tb-installed-type-icon {
width: 100px; font-size: 16px;
min-width: 100px; width: 16px;
max-width: 100px; height: 16px;
} }
.mat-column-createdTime { .tb-type-widget { color: #2e7d32; }
width: 100px; .tb-type-dashboard { color: #1565c0; }
min-width: 100px; .tb-type-solution-template { color: #2666a9; }
max-width: 100px; .tb-type-calc-field { color: #006d92; }
} .tb-type-rule-chain { color: #95694b; }
.tb-type-device { color: #4b8a79; }
.mat-column-updates { // Updates column
width: 200px; .tb-installed-update-available {
min-width: 200px; display: inline-flex;
max-width: 200px; align-items: center;
} gap: 4px;
font-size: 13px;
color: rgba(0, 0, 0, 0.76);
margin-right: 8px;
.mat-column-actions { mat-icon {
width: 144px; font-size: 16px;
min-width: 144px; width: 16px;
max-width: 144px; height: 16px;
text-align: right;
white-space: nowrap;
} }
} }
.tb-iot-hub-type-chip { .tb-installed-update-btn {
padding: 2px 10px;
border-radius: 12px;
font-size: 12px; font-size: 12px;
font-weight: 500;
white-space: nowrap;
&.tb-type-widget { background: #e3f2fd; color: #1565c0; }
&.tb-type-dashboard { background: #e8f5e9; color: #2e7d32; }
&.tb-type-calc-field { background: #fff3e0; color: #e65100; }
&.tb-type-rule-chain { background: #f3e5f5; color: #7b1fa2; }
&.tb-type-device { background: #eceff1; color: #455a64; }
&.tb-type-solution-template { background: #e0f7fa; color: #00695c; }
} }
.tb-iot-hub-up-to-date { .tb-installed-up-to-date {
color: #2e7d32; display: inline-flex;
align-items: center;
gap: 4px;
font-size: 13px; font-size: 13px;
font-weight: 500; color: #198038;
}
.tb-iot-hub-update-button { mat-icon {
font-size: 12px; font-size: 16px;
line-height: 28px; width: 16px;
height: 28px; height: 16px;
padding: 0 12px; }
}
& + .tb-iot-hub-update-button { // Actions column
margin-left: 8px; .tb-installed-actions {
display: flex;
align-items: center;
gap: 0;
justify-content: flex-end;
.mat-mdc-icon-button {
width: 36px;
height: 36px;
padding: 6px;
--mdc-icon-button-state-layer-size: 36px;
--mdc-icon-button-icon-color: rgba(0, 0, 0, 0.54);
color: rgba(0, 0, 0, 0.54);
} }
} }
.tb-iot-hub-installed-empty { // Loading / Empty
padding: 48px 24px; .tb-installed-loading {
display: flex;
justify-content: center;
padding: 48px 0;
}
.tb-installed-empty {
text-align: center; text-align: center;
padding: 48px 0;
color: rgba(0, 0, 0, 0.54); color: rgba(0, 0, 0, 0.54);
font-size: 14px; font-size: 14px;
} }

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

@ -35,6 +35,7 @@ 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 './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';
@Component({ @Component({
selector: 'tb-iot-hub-installed-items', selector: 'tb-iot-hub-installed-items',
@ -56,6 +57,11 @@ export class TbIotHubInstalledItemsComponent implements OnInit, AfterViewInit {
updatesChecked = false; updatesChecked = false;
isCheckingUpdates = false; isCheckingUpdates = false;
// Type filter
filterOpen = false;
activeTypeFilters = new Set<string>();
allItemTypes: string[] = ['WIDGET', 'DASHBOARD', 'SOLUTION_TEMPLATE', 'CALCULATED_FIELD', 'RULE_CHAIN', 'DEVICE'];
private searchSubject = new Subject<string>(); private searchSubject = new Subject<string>();
@ViewChild(MatSort, {static: true}) sort: MatSort; @ViewChild(MatSort, {static: true}) sort: MatSort;
@ -104,14 +110,60 @@ export class TbIotHubInstalledItemsComponent implements OnInit, AfterViewInit {
this.loadData(); this.loadData();
} }
toggleFilter(): void {
this.filterOpen = !this.filterOpen;
}
isTypeFilterActive(type: string): boolean {
return this.activeTypeFilters.has(type);
}
toggleTypeFilter(type: string): void {
if (this.activeTypeFilters.has(type)) {
this.activeTypeFilters.delete(type);
} else {
this.activeTypeFilters.add(type);
}
this.pageIndex = 0;
this.loadData();
}
removeTypeFilter(type: string): void {
this.activeTypeFilters.delete(type);
this.pageIndex = 0;
this.loadData();
}
clearAllFilters(): void {
this.activeTypeFilters.clear();
this.pageIndex = 0;
this.loadData();
}
hasActiveFilters(): boolean {
return this.activeTypeFilters.size > 0;
}
getItemTypeIcon(itemType: string): string {
switch (itemType) {
case 'WIDGET': return 'widgets';
case 'DASHBOARD': return 'dashboard';
case 'SOLUTION_TEMPLATE': return 'integration_instructions';
case 'CALCULATED_FIELD': return 'functions';
case 'RULE_CHAIN': return 'settings_ethernet';
case 'DEVICE': return 'memory';
default: return 'category';
}
}
deleteItem(item: IotHubInstalledItem): void { deleteItem(item: IotHubInstalledItem): void {
this.dialogService.confirm( const dialogRef = this.dialog.open(TbIotHubDeleteDialogComponent, {
this.translate.instant('iot-hub.remove-installed-item-title'), panelClass: ['tb-dialog'],
this.translate.instant('iot-hub.remove-installed-item-text', {name: item.itemName}), autoFocus: false,
this.translate.instant('action.no'), data: { itemName: item.itemName, itemType: item.itemType } as IotHubDeleteDialogData
this.translate.instant('action.yes') });
).subscribe(result => { dialogRef.afterClosed().subscribe(confirmed => {
if (result) { if (confirmed) {
this.iotHubApiService.deleteInstalledItem(item.id.id).subscribe({ this.iotHubApiService.deleteInstalledItem(item.id.id).subscribe({
next: () => { next: () => {
this.store.dispatch(new ActionNotificationShow({ this.store.dispatch(new ActionNotificationShow({
@ -260,7 +312,8 @@ export class TbIotHubInstalledItemsComponent implements OnInit, AfterViewInit {
direction: this.sort?.direction === 'asc' ? Direction.ASC : Direction.DESC direction: this.sort?.direction === 'asc' ? Direction.ASC : Direction.DESC
}; };
const pageLink = new PageLink(this.pageSize, this.pageIndex, this.textSearch || null, sortOrder); const pageLink = new PageLink(this.pageSize, this.pageIndex, this.textSearch || null, sortOrder);
this.iotHubApiService.getInstalledItems(pageLink, null, {ignoreLoading: true}).subscribe({ const typeFilters = this.activeTypeFilters.size > 0 ? Array.from(this.activeTypeFilters) : null;
this.iotHubApiService.getInstalledItems(pageLink, typeFilters, {ignoreLoading: true}).subscribe({
next: (data) => { next: (data) => {
this.dataSource = data.data; this.dataSource = data.data;
this.totalElements = data.totalElements; this.totalElements = data.totalElements;

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

@ -3705,6 +3705,8 @@
"search-placeholder": "Search in IoT Hub...", "search-placeholder": "Search in IoT Hub...",
"search": "Search...", "search": "Search...",
"items-per-page": "Items per page", "items-per-page": "Items per page",
"filter": "Filter",
"available": "Available",
"items-page-desc-widgets": "Jumpstart your IoT journey with pre-configured widgets designed for your industry. Deploy proven architectures instantly and focus your energy on what matters: your unique business logic.", "items-page-desc-widgets": "Jumpstart your IoT journey with pre-configured widgets designed for your industry. Deploy proven architectures instantly and focus your energy on what matters: your unique business logic.",
"items-page-desc-dashboards": "Explore dashboards to find pre-built visualization templates and interactive UI layouts you can deploy in minutes to bring your IoT data to life.", "items-page-desc-dashboards": "Explore dashboards to find pre-built visualization templates and interactive UI layouts you can deploy in minutes to bring your IoT data to life.",
"items-page-desc-solution-templates": "Complete IoT solution packages with dashboards, rule chains, and device configurations. Get started with proven architectures.", "items-page-desc-solution-templates": "Complete IoT solution packages with dashboards, rule chains, and device configurations. Get started with proven architectures.",

Loading…
Cancel
Save