Browse Source

Refine installed items page styling and handle dialog results

- Update table type chips, update column, and filter panel to match design
- Add afterClosed handling to viewItemDetails and viewUpdateDetails dialogs
- Reload table data when items are updated or deleted from detail dialog
pull/15508/head
Igor Kulikov 3 months ago
parent
commit
4c7ca5bfd6
  1. 69
      ui-ngx/src/app/modules/home/pages/iot-hub/iot-hub-installed-items.component.html
  2. 166
      ui-ngx/src/app/modules/home/pages/iot-hub/iot-hub-installed-items.component.scss
  3. 16
      ui-ngx/src/app/modules/home/pages/iot-hub/iot-hub-installed-items.component.ts
  4. 4
      ui-ngx/src/app/modules/home/pages/iot-hub/iot-hub-item-detail-dialog.component.html
  5. 2
      ui-ngx/src/assets/locale/locale.constant-en_US.json

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

@ -29,7 +29,7 @@
@if (isCheckingUpdates) {
<mat-spinner diameter="18"></mat-spinner>
} @else {
<mat-icon>sync</mat-icon>
<mat-icon>update</mat-icon>
}
{{ 'iot-hub.check-for-updates' | translate }}
</button>
@ -38,10 +38,22 @@
<!-- Filter + Search row -->
<div class="tb-installed-toolbar">
<div class="tb-installed-filter-area">
<button mat-stroked-button color="primary" class="tb-installed-filter-btn" (click)="toggleFilter()">
<button mat-stroked-button color="primary" class="tb-installed-filter-btn" [matMenuTriggerFor]="filterMenu">
<mat-icon>filter_list</mat-icon>
{{ 'iot-hub.filter' | translate }}
</button>
<mat-menu #filterMenu="matMenu" class="tb-installed-filter-menu" xPosition="after">
@for (type of allItemTypes; track type; let last = $last) {
<div mat-menu-item class="tb-installed-filter-item" (click)="$event.stopPropagation(); toggleTypeFilter(type)">
<mat-checkbox [checked]="isTypeFilterActive(type)">
{{ getItemTypeLabel(type) }}
</mat-checkbox>
</div>
@if (!last) {
<mat-divider></mat-divider>
}
}
</mat-menu>
@if (hasActiveFilters()) {
<mat-chip-set class="tb-installed-filter-chips">
@for (type of activeTypeFilters; track type) {
@ -65,18 +77,7 @@
</div>
</div>
<!-- Filter dropdown -->
@if (filterOpen) {
<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 -->
<!-- Table + Paginator wrapped in border -->
<div class="tb-installed-table-container">
<table mat-table [dataSource]="dataSource" matSort matSortActive="createdTime" matSortDirection="desc">
<!-- Name -->
@ -110,18 +111,20 @@
<!-- Updates -->
<ng-container matColumnDef="updates">
<th mat-header-cell *matHeaderCellDef>{{ 'iot-hub.updates' | translate }}</th>
<th mat-header-cell *matHeaderCellDef>@if (updatesChecked) { {{ 'iot-hub.updates' | translate }} }</th>
<td mat-cell *matCellDef="let item">
@if (updatesChecked) {
@if (getPublishedVersionInfo(item); as pubInfo) {
@if (pubInfo.publishedVersionId !== item.itemVersionId) {
<span class="tb-installed-update-available">
<mat-icon>arrow_upward</mat-icon>
v{{ pubInfo.publishedVersion }} {{ 'iot-hub.available' | translate }}
</span>
<button mat-stroked-button color="warn" class="tb-installed-update-btn" (click)="updateItem(item, pubInfo)">
{{ 'iot-hub.update' | translate }}
</button>
<div class="tb-installed-update-row">
<button class="tb-installed-version-btn" (click)="viewUpdateDetails(pubInfo, item)">
<mat-icon>arrow_upward</mat-icon>
v{{ pubInfo.publishedVersion }} {{ 'iot-hub.available' | translate }}
</button>
<button mat-stroked-button color="warn" class="tb-installed-update-btn" (click)="updateItem(item, pubInfo)">
{{ 'iot-hub.update' | translate }}
</button>
</div>
} @else {
<span class="tb-installed-up-to-date">
<mat-icon>check</mat-icon>
@ -146,7 +149,7 @@
<button mat-icon-button (click)="viewItemDetails(item)" matTooltip="{{ 'iot-hub.view-item-details' | translate }}">
<mat-icon>info_outline</mat-icon>
</button>
<button mat-icon-button (click)="openEntity(item)" matTooltip="{{ 'iot-hub.open-installed-entity' | translate }}">
<button mat-icon-button (click)="openEntity(item)" matTooltip="{{ (item.itemType === 'SOLUTION_TEMPLATE' ? 'iot-hub.goto-main-dashboard' : 'iot-hub.open-item-type') | translate:{ type: getItemTypeLabel(item.itemType) } }}">
<mat-icon>open_in_new</mat-icon>
</button>
<button mat-icon-button (click)="deleteItem(item)" matTooltip="{{ 'iot-hub.remove' | translate }}">
@ -171,15 +174,15 @@
{{ 'iot-hub.no-installed-items' | translate }}
</div>
}
</div>
<!-- Paginator -->
<mat-paginator
[length]="totalElements"
[pageSize]="pageSize"
[pageIndex]="pageIndex"
[pageSizeOptions]="[10, 25, 50]"
(page)="onPageChange($event)"
showFirstLastButtons>
</mat-paginator>
<!-- Paginator inside border -->
<mat-paginator
[length]="totalElements"
[pageSize]="pageSize"
[pageIndex]="pageIndex"
[pageSizeOptions]="[10, 25, 50]"
(page)="onPageChange($event)"
showFirstLastButtons>
</mat-paginator>
</div>
</div>

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

@ -24,7 +24,7 @@
.tb-installed-items-page {
background: white;
border-radius: 8px;
padding: 40px 40px 24px;
padding: 40px 40px 40px;
}
// Back button
@ -41,7 +41,7 @@
}
.tb-installed-title {
font-size: 40px;
font-size: 36px;
font-weight: 500;
line-height: 48px;
color: rgba(0, 0, 0, 0.87);
@ -89,6 +89,26 @@
}
}
// Filter menu panel
::ng-deep .tb-installed-filter-menu {
.mat-mdc-menu-content {
padding: 0;
}
.tb-installed-filter-item {
height: 40px;
padding: 0 16px;
mat-checkbox {
width: 100%;
}
}
mat-divider {
margin: 0;
}
}
// Search field
.tb-installed-search {
width: 320px;
@ -125,92 +145,136 @@
}
}
// Filter dropdown
.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-checkbox {
display: block;
padding: 8px 16px;
&:hover {
background: rgba(0, 0, 0, 0.04);
}
}
}
// Table
// Table + paginator container unified border
.tb-installed-table-container {
border: 1px solid rgba(0, 0, 0, 0.12);
border-radius: 4px;
border-radius: 8px;
overflow: hidden;
table {
width: 100%;
}
// Header cells Design: 16px Medium, rgba(0,0,0,0.87), tracking 0.25
th.mat-mdc-header-cell {
font-size: 16px;
font-weight: 500;
line-height: 24px;
letter-spacing: 0.25px;
color: rgba(0, 0, 0, 0.87);
}
// Body cells Design: 14px Regular, rgba(0,0,0,0.76), tracking 0.2
td.mat-mdc-cell {
font-size: 14px;
font-weight: 400;
letter-spacing: 0.2px;
color: rgba(0, 0, 0, 0.76);
}
::ng-deep .mat-mdc-paginator {
border-top: none;
}
}
// Type cell with colored icon + text
// Type chip Design: pill, h-32, rounded-16, px-12 py-6, gap-4, bg at 6% opacity
.tb-installed-type {
display: inline-flex;
align-items: center;
gap: 4px;
height: 32px;
padding: 6px 12px;
border-radius: 16px;
font-size: 14px;
font-weight: 500;
font-weight: 400;
line-height: 20px;
letter-spacing: 0.2px;
}
.tb-installed-type-icon {
font-size: 16px;
width: 16px;
height: 16px;
font-size: 18px;
width: 18px;
height: 18px;
color: inherit !important;
--mat-icon-color: currentColor;
}
.tb-type-widget { color: #2e7d32; }
.tb-type-dashboard { color: #1565c0; }
.tb-type-solution-template { color: #2666a9; }
.tb-type-calc-field { color: #006d92; }
.tb-type-rule-chain { color: #95694b; }
.tb-type-device { color: #4b8a79; }
.tb-type-widget { color: rgb(32, 115, 61); background: rgba(46, 166, 88, 0.06); }
.tb-type-dashboard { color: rgb(61, 76, 166); background: rgba(61, 76, 166, 0.06); }
.tb-type-solution-template { color: rgb(0, 137, 186); background: rgba(0, 137, 186, 0.06); }
.tb-type-calc-field { color: rgb(40, 120, 148); background: rgba(40, 120, 148, 0.06); }
.tb-type-rule-chain { color: rgb(178, 121, 29); background: rgba(179, 121, 29, 0.06); }
.tb-type-device { color: rgb(24, 146, 110); background: rgba(24, 146, 110, 0.06); }
// Updates column Design: 14px Medium, tracking 0.25, gap-8, icons 18px
// Update row flex container for version btn + update btn
.tb-installed-update-row {
display: flex;
align-items: center;
gap: 12px;
}
// Updates column
.tb-installed-update-available {
// "vX.X.X Available" button Design: border rgba(0,0,0,0.12), rounded-4, px-12 py-6, gap-8
.tb-installed-version-btn {
display: inline-flex;
align-items: center;
gap: 4px;
font-size: 13px;
gap: 8px;
font-size: 14px;
font-weight: 500;
line-height: 20px;
letter-spacing: 0.25px;
color: rgba(0, 0, 0, 0.76);
margin-right: 8px;
background: none;
border: 1px solid rgba(0, 0, 0, 0.12);
border-radius: 4px;
padding: 6px 12px;
cursor: pointer;
font-family: inherit;
mat-icon {
font-size: 16px;
width: 16px;
height: 16px;
font-size: 18px;
width: 18px;
height: 18px;
color: inherit !important;
--mat-icon-color: currentColor;
}
&:hover {
background: rgba(0, 0, 0, 0.04);
}
}
.tb-installed-update-btn {
font-size: 12px;
// "Update" button Design: same height as available btn, border matches text color
.tb-installed-update-btn.mat-mdc-outlined-button {
font-size: 14px;
font-weight: 500;
line-height: 20px;
letter-spacing: 0.25px;
padding: 6px 12px;
height: auto;
--mat-button-outlined-outline-color: var(--mat-button-outlined-label-text-color);
}
// "Up to date" Design: 14px Medium, #198038, gap-8, py-6
.tb-installed-up-to-date {
display: inline-flex;
align-items: center;
gap: 4px;
font-size: 13px;
gap: 8px;
font-size: 14px;
font-weight: 500;
line-height: 20px;
letter-spacing: 0.25px;
color: #198038;
padding: 6px 0;
mat-icon {
font-size: 16px;
width: 16px;
height: 16px;
font-size: 18px;
width: 18px;
height: 18px;
color: inherit !important;
--mat-icon-color: currentColor;
}
}

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

@ -204,14 +204,20 @@ export class TbIotHubInstalledItemsComponent implements OnInit, AfterViewInit {
viewItemDetails(item: IotHubInstalledItem): void {
this.iotHubApiService.getVersionInfo(item.itemVersionId, {ignoreLoading: true}).subscribe(versionView => {
this.dialog.open(TbIotHubItemDetailDialogComponent, {
const dialogRef = this.dialog.open(TbIotHubItemDetailDialogComponent, {
panelClass: ['tb-dialog', 'tb-fullscreen-dialog'],
autoFocus: false,
data: {
item: versionView,
iotHubApiService: this.iotHubApiService,
installedItem: item
} as IotHubItemDetailDialogData
});
dialogRef.afterClosed().subscribe(result => {
if (result === 'updated' || result === 'deleted') {
this.loadData();
}
});
});
}
@ -271,14 +277,20 @@ export class TbIotHubInstalledItemsComponent implements OnInit, AfterViewInit {
viewUpdateDetails(publishedInfo: ItemPublishedVersionInfo, installedItem: IotHubInstalledItem): void {
this.iotHubApiService.getVersionInfo(publishedInfo.publishedVersionId, {ignoreLoading: true}).subscribe(versionView => {
this.dialog.open(TbIotHubItemDetailDialogComponent, {
const dialogRef = this.dialog.open(TbIotHubItemDetailDialogComponent, {
panelClass: ['tb-dialog', 'tb-fullscreen-dialog'],
autoFocus: false,
data: {
item: versionView,
iotHubApiService: this.iotHubApiService,
installedItem
} as IotHubItemDetailDialogData
});
dialogRef.afterClosed().subscribe(result => {
if (result === 'updated' || result === 'deleted') {
this.loadData();
}
});
});
}

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

@ -64,7 +64,7 @@
</span>
<a class="dlg-info-link" (click)="openEntityDetails()">
<mat-icon>open_in_new</mat-icon>
{{ 'iot-hub.open-item-type-details' | translate:{ type: getTypeLabel() } }}
{{ 'iot-hub.open-item-type' | translate:{ type: getTypeLabel() } }}
</a>
</div>
}
@ -175,7 +175,7 @@
</span>
<a class="dlg-info-link" (click)="openEntityDetails()">
<mat-icon>open_in_new</mat-icon>
{{ 'iot-hub.open-item-type-details' | translate:{ type: getTypeLabel() } }}
{{ 'iot-hub.open-item-type' | translate:{ type: getTypeLabel() } }}
</a>
</div>
}

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

@ -3868,7 +3868,7 @@
"installed-item-remove-error": "Failed to remove '{{name}}'",
"open-installed-entity": "Open installed entity",
"view-item-details": "View item details",
"open-item-type-details": "Open {{type}} details",
"open-item-type": "Open {{type}}",
"back-to-marketplace": "Back to Marketplace",
"device-install-connection": "Connection: {{type}}",
"device-install-entity-error": "Failed to create {{name}}: {{error}}",

Loading…
Cancel
Save