From da86dcc23404503f91d1dad337b082cf791770ec Mon Sep 17 00:00:00 2001 From: Igor Kulikov Date: Wed, 10 Jun 2026 17:59:39 +0300 Subject: [PATCH] refactor(iot-hub): centralise item-type icons and chip colors MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Add itemTypeIcons + getItemTypeIcon helper to iot-hub-item.models, mirroring mp-item.models in the iot-hub project: widgets / dashboard / apps / mdi:function-variant / settings_ethernet / mdi:bell-cog / devices_other with `category` as the fallback. - Replace every hand-rolled item-type → icon switch with calls to getItemTypeIcon: item detail dialog (getTypeIcon / getCompactIcon fallback path), installed-items table (getItemTypeIcon), iot-hub-home (getCompactIcon / getItemTypeIcon), item-card (getPlaceholderIcon default + per-type fallbacks), item-link-card (getCompactIcon / getTypeIcon). - Swap the remaining mat-icon placeholders that render an item-type icon to tb-icon (installed-items table chip, home search popup thumb placeholder, item-link-card thumb fallback). Generic status / action icons stay on mat-icon. - Refresh the colored item-type chip palette in the installed items table to match itemTypeChipColors from mp-item.models, and add the previously-missing tb-type-alarm-rule rule so that chip picks up the new orange palette instead of falling through to no background. --- ...t-hub-installed-items-table.component.html | 2 +- ...t-hub-installed-items-table.component.scss | 14 ++++++----- ...iot-hub-installed-items-table.component.ts | 13 ++-------- .../iot-hub/iot-hub-item-card.component.ts | 22 ++++++++++------- .../iot-hub-item-detail-dialog.component.ts | 19 ++++----------- .../iot-hub-item-link-card.component.html | 2 +- .../iot-hub-item-link-card.component.ts | 24 +++---------------- .../pages/iot-hub/iot-hub-home.component.html | 2 +- .../pages/iot-hub/iot-hub-home.component.ts | 21 +++------------- .../models/iot-hub/iot-hub-item.models.ts | 17 +++++++++++++ 10 files changed, 54 insertions(+), 82 deletions(-) diff --git a/ui-ngx/src/app/modules/home/components/iot-hub/iot-hub-installed-items-table.component.html b/ui-ngx/src/app/modules/home/components/iot-hub/iot-hub-installed-items-table.component.html index 7bbff19a85..545d219596 100644 --- a/ui-ngx/src/app/modules/home/components/iot-hub/iot-hub-installed-items-table.component.html +++ b/ui-ngx/src/app/modules/home/components/iot-hub/iot-hub-installed-items-table.component.html @@ -29,7 +29,7 @@ {{ 'iot-hub.item-type' | translate }} - {{ getItemTypeIcon(item.itemType) }} + {{ getItemTypeIcon(item.itemType) }} {{ getItemTypeLabel(item.itemType) }} diff --git a/ui-ngx/src/app/modules/home/components/iot-hub/iot-hub-installed-items-table.component.scss b/ui-ngx/src/app/modules/home/components/iot-hub/iot-hub-installed-items-table.component.scss index d83bb3e192..1edef65112 100644 --- a/ui-ngx/src/app/modules/home/components/iot-hub/iot-hub-installed-items-table.component.scss +++ b/ui-ngx/src/app/modules/home/components/iot-hub/iot-hub-installed-items-table.component.scss @@ -122,12 +122,14 @@ --mat-icon-color: currentColor; } -.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); } +// Colors mirror itemTypeChipColors from the iot-hub mp-item.models. +.tb-type-device { color: #3d4ca6; background: rgba(61, 76, 166, 0.06); } +.tb-type-solution-template { color: #2c6cb4; background: rgba(44, 108, 180, 0.06); } +.tb-type-widget { color: #20733d; background: rgba(46, 166, 88, 0.06); } +.tb-type-calc-field { color: #3db5e0; background: rgba(61, 181, 224, 0.06); } +.tb-type-alarm-rule { color: #d7702f; background: rgba(215, 112, 47, 0.06); } +.tb-type-rule-chain { color: #aa5be3; background: rgba(170, 91, 227, 0.06); } +.tb-type-dashboard { color: #607d8b; background: rgba(96, 125, 139, 0.06); } // "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 { diff --git a/ui-ngx/src/app/modules/home/components/iot-hub/iot-hub-installed-items-table.component.ts b/ui-ngx/src/app/modules/home/components/iot-hub/iot-hub-installed-items-table.component.ts index 09917528be..b625d96e2b 100644 --- a/ui-ngx/src/app/modules/home/components/iot-hub/iot-hub-installed-items-table.component.ts +++ b/ui-ngx/src/app/modules/home/components/iot-hub/iot-hub-installed-items-table.component.ts @@ -44,7 +44,7 @@ import { ItemPublishedVersionInfo } from '@shared/models/iot-hub/iot-hub-installed-item.models'; import { MpItemVersionView } from '@shared/models/iot-hub/iot-hub-version.models'; -import { ItemType, itemTypeTranslations } from '@shared/models/iot-hub/iot-hub-item.models'; +import { getItemTypeIcon, ItemType, itemTypeTranslations } from '@shared/models/iot-hub/iot-hub-item.models'; import { EntityType } from '@shared/models/entity-type.models'; import { getEntityDetailsPageURL } from '@core/utils'; import { IotHubActionsService } from '@home/components/iot-hub/iot-hub-actions.service'; @@ -153,16 +153,7 @@ export class TbIotHubInstalledItemsTableComponent implements OnInit, OnChanges, } 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 'ALARM_RULE': return 'notification_important'; - case 'RULE_CHAIN': return 'settings_ethernet'; - case 'DEVICE': return 'memory'; - default: return 'category'; - } + return getItemTypeIcon(itemType); } deleteItem(item: IotHubInstalledItem): void { diff --git a/ui-ngx/src/app/modules/home/components/iot-hub/iot-hub-item-card.component.ts b/ui-ngx/src/app/modules/home/components/iot-hub/iot-hub-item-card.component.ts index 38c1e26dcf..bdf50acddf 100644 --- a/ui-ngx/src/app/modules/home/components/iot-hub/iot-hub-item-card.component.ts +++ b/ui-ngx/src/app/modules/home/components/iot-hub/iot-hub-item-card.component.ts @@ -16,7 +16,7 @@ import { Component, EventEmitter, Input, Output } from '@angular/core'; import { MpItemVersionView, cfTypeTranslations, cfTypeIcons, ruleChainTypeTranslations, widgetTypeTranslations } from '@shared/models/iot-hub/iot-hub-version.models'; -import { ItemType } from '@shared/models/iot-hub/iot-hub-item.models'; +import { getItemTypeIcon, ItemType } 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'; @@ -69,17 +69,21 @@ export class TbIotHubItemCardComponent { getPlaceholderIcon(): string { switch (this.item.type) { - case ItemType.WIDGET: return 'widgets'; - case ItemType.DASHBOARD: return 'dashboard'; - case ItemType.SOLUTION_TEMPLATE: return 'integration_instructions'; case ItemType.CALCULATED_FIELD: - return this.item.icon || cfTypeIcons.get(this.item.dataDescriptor?.cfType) || 'functions'; + return this.item.icon + || cfTypeIcons.get(this.item.dataDescriptor?.cfType) + || getItemTypeIcon(ItemType.CALCULATED_FIELD); case ItemType.ALARM_RULE: - return this.item.icon || 'notification_important'; + return this.item.icon || getItemTypeIcon(ItemType.ALARM_RULE); case ItemType.RULE_CHAIN: - return this.item.icon || (this.item.dataDescriptor?.ruleChainType === 'EDGE' ? 'router' : 'device_hub'); - case ItemType.DEVICE: return 'memory'; - default: return 'extension'; + return this.item.icon + || (this.item.dataDescriptor?.ruleChainType === 'EDGE' + ? 'router' + : (this.item.dataDescriptor?.ruleChainType === 'CORE' + ? 'device_hub' + : getItemTypeIcon(ItemType.RULE_CHAIN))); + default: + return getItemTypeIcon(this.item.type); } } diff --git a/ui-ngx/src/app/modules/home/components/iot-hub/iot-hub-item-detail-dialog.component.ts b/ui-ngx/src/app/modules/home/components/iot-hub/iot-hub-item-detail-dialog.component.ts index c770045ddc..382f2d0f88 100644 --- a/ui-ngx/src/app/modules/home/components/iot-hub/iot-hub-item-detail-dialog.component.ts +++ b/ui-ngx/src/app/modules/home/components/iot-hub/iot-hub-item-detail-dialog.component.ts @@ -21,7 +21,7 @@ import { Store } from '@ngrx/store'; import { AppState } from '@core/core.state'; import { DialogComponent } from '@shared/components/dialog.component'; import { MpItemVersionView, cfTypeTranslations, cfTypeIcons, ruleChainTypeTranslations, widgetTypeTranslations, NodeInfo } from '@shared/models/iot-hub/iot-hub-version.models'; -import { ItemType, itemTypeTranslations } from '@shared/models/iot-hub/iot-hub-item.models'; +import { getItemTypeIcon, ItemType, itemTypeTranslations } from '@shared/models/iot-hub/iot-hub-item.models'; import { IotHubInstalledItem } from '@shared/models/iot-hub/iot-hub-installed-item.models'; import { IotHubApiService } from '@core/http/iot-hub-api.service'; import { TranslateService } from '@ngx-translate/core'; @@ -103,16 +103,7 @@ export class TbIotHubItemDetailDialogComponent extends DialogComponent } @else { - {{ getTypeIcon() }} + {{ getTypeIcon() }} } } diff --git a/ui-ngx/src/app/modules/home/components/iot-hub/iot-hub-item-link-card/iot-hub-item-link-card.component.ts b/ui-ngx/src/app/modules/home/components/iot-hub/iot-hub-item-link-card/iot-hub-item-link-card.component.ts index c2ca0ba1ea..743f02bea4 100644 --- a/ui-ngx/src/app/modules/home/components/iot-hub/iot-hub-item-link-card/iot-hub-item-link-card.component.ts +++ b/ui-ngx/src/app/modules/home/components/iot-hub/iot-hub-item-link-card/iot-hub-item-link-card.component.ts @@ -17,7 +17,7 @@ import { Component, Input, OnInit } from '@angular/core'; import { IotHubApiService } from '@core/http/iot-hub-api.service'; import { MpItemVersionView } from '@shared/models/iot-hub/iot-hub-version.models'; -import { ItemType } from '@shared/models/iot-hub/iot-hub-item.models'; +import { getItemTypeIcon, ItemType } from '@shared/models/iot-hub/iot-hub-item.models'; type CardState = 'loading' | 'loaded' | 'unavailable'; @@ -71,29 +71,11 @@ export class TbIotHubItemLinkCardComponent implements OnInit { getCompactIcon(): string { const item = this.item!; - if (item.icon) { - return item.icon; - } - switch (item.type) { - case ItemType.CALCULATED_FIELD: return 'functions'; - case ItemType.ALARM_RULE: return 'notification_important'; - case ItemType.RULE_CHAIN: return 'account_tree'; - default: return 'category'; - } + return item.icon || getItemTypeIcon(item.type); } getTypeIcon(): string { - const item = this.item!; - switch (item.type) { - case ItemType.WIDGET: return 'widgets'; - case ItemType.DASHBOARD: return 'dashboard'; - case ItemType.SOLUTION_TEMPLATE: return 'integration_instructions'; - case ItemType.CALCULATED_FIELD: return 'functions'; - case ItemType.ALARM_RULE: return 'notification_important'; - case ItemType.RULE_CHAIN: return 'account_tree'; - case ItemType.DEVICE: return 'memory'; - default: return 'category'; - } + return getItemTypeIcon(this.item!.type); } getCompactColor(): string { diff --git a/ui-ngx/src/app/modules/home/pages/iot-hub/iot-hub-home.component.html b/ui-ngx/src/app/modules/home/pages/iot-hub/iot-hub-home.component.html index 682411d884..f9f7285c3b 100644 --- a/ui-ngx/src/app/modules/home/pages/iot-hub/iot-hub-home.component.html +++ b/ui-ngx/src/app/modules/home/pages/iot-hub/iot-hub-home.component.html @@ -109,7 +109,7 @@ @if (getItemImage(item); as imgUrl) { } @else { - {{ getItemTypeIcon(item.type) }} + {{ getItemTypeIcon(item.type) }} } } diff --git a/ui-ngx/src/app/modules/home/pages/iot-hub/iot-hub-home.component.ts b/ui-ngx/src/app/modules/home/pages/iot-hub/iot-hub-home.component.ts index 72a5513056..c8fff1ea7e 100644 --- a/ui-ngx/src/app/modules/home/pages/iot-hub/iot-hub-home.component.ts +++ b/ui-ngx/src/app/modules/home/pages/iot-hub/iot-hub-home.component.ts @@ -25,7 +25,7 @@ import { MediaBreakpoints } from '@shared/models/constants'; import { PageLink } from '@shared/models/page/page-link'; import { Direction, SortOrder } from '@shared/models/page/sort-order'; import { MpItemVersionQuery, MpItemVersionView } from '@shared/models/iot-hub/iot-hub-version.models'; -import { ItemType, itemTypeTranslations } from '@shared/models/iot-hub/iot-hub-item.models'; +import { getItemTypeIcon, ItemType, itemTypeTranslations } from '@shared/models/iot-hub/iot-hub-item.models'; import { IotHubInstalledItem } from '@shared/models/iot-hub/iot-hub-installed-item.models'; import { IotHubApiService } from '@core/http/iot-hub-api.service'; import { TranslateService } from '@ngx-translate/core'; @@ -266,15 +266,7 @@ export class TbIotHubHomeComponent implements OnInit, OnDestroy { } getCompactIcon(item: MpItemVersionView): string { - if (item.icon) { - return item.icon; - } - switch (item.type) { - case ItemType.CALCULATED_FIELD: return 'functions'; - case ItemType.ALARM_RULE: return 'notification_important'; - case ItemType.RULE_CHAIN: return 'settings_ethernet'; - default: return 'category'; - } + return item.icon || getItemTypeIcon(item.type); } getItemImage(item: MpItemVersionView): string | null { @@ -282,14 +274,7 @@ export class TbIotHubHomeComponent implements OnInit, OnDestroy { } getItemTypeIcon(type: ItemType): string { - switch (type) { - case ItemType.WIDGET: return 'widgets'; - case ItemType.DASHBOARD: return 'dashboard'; - case ItemType.SOLUTION_TEMPLATE: return 'integration_instructions'; - case ItemType.ALARM_RULE: return 'notification_important'; - case ItemType.DEVICE: return 'memory'; - default: return 'category'; - } + return getItemTypeIcon(type); } getSearchGroupLabel(type: ItemType): string { diff --git a/ui-ngx/src/app/shared/models/iot-hub/iot-hub-item.models.ts b/ui-ngx/src/app/shared/models/iot-hub/iot-hub-item.models.ts index 6451bc5d52..6c528384c4 100644 --- a/ui-ngx/src/app/shared/models/iot-hub/iot-hub-item.models.ts +++ b/ui-ngx/src/app/shared/models/iot-hub/iot-hub-item.models.ts @@ -36,6 +36,23 @@ export const itemTypeTranslations = new Map( ] ); +// Canonical icon lookup per item type. Values are tb-icon +// identifiers (Material symbol names or `mdi:*` strings) and should +// be used everywhere an icon is rendered for an item type so the +// mapping stays consistent across the app. +export const itemTypeIcons: Record = { + [ItemType.WIDGET]: 'widgets', + [ItemType.DASHBOARD]: 'dashboard', + [ItemType.SOLUTION_TEMPLATE]: 'apps', + [ItemType.CALCULATED_FIELD]: 'mdi:function-variant', + [ItemType.RULE_CHAIN]: 'settings_ethernet', + [ItemType.ALARM_RULE]: 'mdi:bell-cog', + [ItemType.DEVICE]: 'devices_other' +}; + +export const getItemTypeIcon = (type?: string | null): string => + type && itemTypeIcons[type] ? itemTypeIcons[type] : 'category'; + /** * Item types discoverable to creators in the marketplace UI. * DASHBOARD is intentionally absent (IoT Hub no longer accepts Dashboard contributions).