From 94f21cb6a1d76fd8a9c0c8d4431e0a17657014ec Mon Sep 17 00:00:00 2001 From: Igor Kulikov Date: Mon, 19 Aug 2024 15:19:57 +0300 Subject: [PATCH 1/9] UI: Menu refactoring. --- ui-ngx/src/app/core/services/menu.models.ts | 771 +++++++++++++++++- ui-ngx/src/app/core/services/menu.service.ts | 642 +-------------- .../home/pages/admin/admin-routing.module.ts | 52 +- .../home/pages/alarm/alarm-routing.module.ts | 4 +- .../api-usage/api-usage-routing.module.ts | 4 +- .../asset-profile-routing.module.ts | 4 +- .../home/pages/asset/asset-routing.module.ts | 4 +- .../audit-log/audit-log-routing.module.ts | 4 +- .../pages/customer/customer-routing.module.ts | 4 +- .../dashboard/dashboard-routing.module.ts | 4 +- .../device-profile-routing.module.ts | 4 +- .../pages/device/device-routing.module.ts | 4 +- .../home/pages/edge/edge-routing.module.ts | 10 +- .../entity-view/entity-view-routing.module.ts | 4 +- .../pages/features/features-routing.module.ts | 4 +- .../home-links/home-links-routing.module.ts | 4 +- .../notification-routing.module.ts | 19 +- .../ota-update/ota-update-routing.module.ts | 4 +- .../pages/profiles/profiles-routing.module.ts | 4 +- .../rulechain/rulechain-routing.module.ts | 5 +- .../tenant-profile-routing.module.ts | 4 +- .../pages/tenant/tenant-routing.module.ts | 4 +- .../home/pages/vc/vc-routing.module.ts | 4 +- .../widget/widget-library-routing.module.ts | 10 +- .../components/breadcrumb.component.html | 4 +- .../shared/components/breadcrumb.component.ts | 36 +- .../src/app/shared/components/breadcrumb.ts | 6 +- 27 files changed, 889 insertions(+), 734 deletions(-) diff --git a/ui-ngx/src/app/core/services/menu.models.ts b/ui-ngx/src/app/core/services/menu.models.ts index b1278d4db3..703397fee1 100644 --- a/ui-ngx/src/app/core/services/menu.models.ts +++ b/ui-ngx/src/app/core/services/menu.models.ts @@ -14,11 +14,14 @@ /// limitations under the License. /// -import { HasUUID } from '@shared/models/id/has-uuid'; +import { AuthState } from '@core/auth/auth.models'; +import { Authority } from '@shared/models/authority.enum'; +import { deepClone } from '@core/utils'; export declare type MenuSectionType = 'link' | 'toggle'; -export interface MenuSection extends HasUUID{ +export interface MenuSection { + id: MenuId | string; name: string; fullName?: string; type: MenuSectionType; @@ -30,6 +33,11 @@ export interface MenuSection extends HasUUID{ rootOnly?: boolean; } +export interface MenuReference { + id: MenuId; + pages?: Array; +} + export interface HomeSection { name: string; places: Array; @@ -40,3 +48,762 @@ export interface HomeSectionPlace { icon: string; path: string; } + +export enum MenuId { + home = 'home', + tenants = 'tenants', + tenant_profiles = 'tenant_profiles', + resources = 'resources', + widget_library = 'widget_library', + widget_types = 'widget_types', + widgets_bundles = 'widgets_bundles', + images = 'images', + scada_symbols = 'scada_symbols', + resources_library = 'resources_library', + notifications_center = 'notifications_center', + notification_inbox = 'notification_inbox', + notification_sent = 'notification_sent', + notification_recipients = 'notification_recipients', + notification_templates = 'notification_templates', + notification_rules = 'notification_rules', + settings = 'settings', + general = 'general', + mail_server = 'mail_server', + home_settings = 'home_settings', + notification_settings = 'notification_settings', + repository_settings = 'repository_settings', + auto_commit_settings = 'auto_commit_settings', + queues = 'queues', + mobile_app_settings = 'mobile_app_settings', + security_settings = 'security_settings', + security_settings_general = 'security_settings_general', + two_fa = '2fa', + oauth2 = 'oauth2', + audit_log = 'audit_log', + alarms = 'alarms', + dashboards = 'dashboards', + entities = 'entities', + devices = 'devices', + assets = 'assets', + entity_views = 'entity_views', + profiles = 'profiles', + device_profiles = 'device_profiles', + asset_profiles = 'asset_profiles', + customers = 'customers', + rule_chains = 'rule_chains', + edge_management = 'edge_management', + edges = 'edges', + rulechain_templates = 'rulechain_templates', + features = 'features', + otaUpdates = 'otaUpdates', + version_control = 'version_control', + api_usage = 'api_usage' +} + +declare type MenuFilter = (authState: AuthState) => boolean; + +const menuSectionMap = new Map([ + [ + MenuId.home, + { + id: MenuId.home, + name: 'home.home', + type: 'link', + path: '/home', + icon: 'home' + } + ], + [ + MenuId.tenants, + { + id: MenuId.tenants, + name: 'tenant.tenants', + type: 'link', + path: '/tenants', + icon: 'supervisor_account' + } + ], + [ + MenuId.tenant_profiles, + { + id: MenuId.tenant_profiles, + name: 'tenant-profile.tenant-profiles', + type: 'link', + path: '/tenantProfiles', + icon: 'mdi:alpha-t-box' + } + ], + [ + MenuId.resources, + { + id: MenuId.resources, + name: 'admin.resources', + type: 'toggle', + path: '/resources', + icon: 'folder' + } + ], + [ + MenuId.widget_library, + { + id: MenuId.widget_library, + name: 'widget.widget-library', + type: 'link', + path: '/resources/widgets-library', + icon: 'now_widgets' + } + ], + [ + MenuId.widget_types, + { + id: MenuId.widget_types, + name: 'widget.widgets', + type: 'link', + path: '/resources/widgets-library/widget-types', + icon: 'now_widgets' + } + ], + [ + MenuId.widgets_bundles, + { + id: MenuId.widgets_bundles, + name: 'widgets-bundle.widgets-bundles', + type: 'link', + path: '/resources/widgets-library/widgets-bundles', + icon: 'now_widgets' + } + ], + [ + MenuId.images, + { + id: MenuId.images, + name: 'image.gallery', + type: 'link', + path: '/resources/images', + icon: 'filter' + } + ], + [ + MenuId.scada_symbols, + { + id: MenuId.scada_symbols, + name: 'scada.symbols', + type: 'link', + path: '/resources/scada-symbols', + icon: 'view_in_ar' + } + ], + [ + MenuId.resources_library, + { + id: MenuId.resources_library, + name: 'resource.resources-library', + type: 'link', + path: '/resources/resources-library', + icon: 'mdi:rhombus-split' + } + ], + [ + MenuId.notifications_center, + { + id: MenuId.notifications_center, + name: 'notification.notification-center', + type: 'link', + path: '/notification', + icon: 'mdi:message-badge' + } + ], + [ + MenuId.notification_inbox, + { + id: MenuId.notification_inbox, + name: 'notification.inbox', + fullName: 'notification.notification-inbox', + type: 'link', + path: '/notification/inbox', + icon: 'inbox' + } + ], + [ + MenuId.notification_sent, + { + id: MenuId.notification_sent, + name: 'notification.sent', + fullName: 'notification.notification-sent', + type: 'link', + path: '/notification/sent', + icon: 'outbox' + } + ], + [ + MenuId.notification_recipients, + { + id: MenuId.notification_recipients, + name: 'notification.recipients', + fullName: 'notification.notification-recipients', + type: 'link', + path: '/notification/recipients', + icon: 'contacts' + } + ], + [ + MenuId.notification_templates, + { + id: MenuId.notification_templates, + name: 'notification.templates', + fullName: 'notification.notification-templates', + type: 'link', + path: '/notification/templates', + icon: 'mdi:message-draw' + } + ], + [ + MenuId.notification_rules, + { + id: MenuId.notification_rules, + name: 'notification.rules', + fullName: 'notification.notification-rules', + type: 'link', + path: '/notification/rules', + icon: 'mdi:message-cog' + } + ], + [ + MenuId.settings, + { + id: MenuId.settings, + name: 'admin.settings', + type: 'link', + path: '/settings', + icon: 'settings' + } + ], + [ + MenuId.general, + { + id: MenuId.general, + name: 'admin.general', + fullName: 'admin.general-settings', + type: 'link', + path: '/settings/general', + icon: 'settings_applications' + } + ], + [ + MenuId.mail_server, + { + id: MenuId.mail_server, + name: 'admin.outgoing-mail', + type: 'link', + path: '/settings/outgoing-mail', + icon: 'mail' + } + ], + [ + MenuId.home_settings, + { + id: MenuId.home_settings, + name: 'admin.home', + fullName: 'admin.home-settings', + type: 'link', + path: '/settings/home', + icon: 'settings_applications' + } + ], + [ + MenuId.notification_settings, + { + id: MenuId.notification_settings, + name: 'admin.notifications', + fullName: 'admin.notifications-settings', + type: 'link', + path: '/settings/notifications', + icon: 'mdi:message-badge' + } + ], + [ + MenuId.repository_settings, + { + id: MenuId.repository_settings, + name: 'admin.repository', + fullName: 'admin.repository-settings', + type: 'link', + path: '/settings/repository', + icon: 'manage_history' + } + ], + [ + MenuId.auto_commit_settings, + { + id: MenuId.auto_commit_settings, + name: 'admin.auto-commit', + fullName: 'admin.auto-commit-settings', + type: 'link', + path: '/settings/auto-commit', + icon: 'settings_backup_restore' + } + ], + [ + MenuId.queues, + { + id: MenuId.queues, + name: 'admin.queues', + type: 'link', + path: '/settings/queues', + icon: 'swap_calls' + } + ], + [ + MenuId.mobile_app_settings, + { + id: MenuId.mobile_app_settings, + name: 'admin.mobile-app.mobile-app', + fullName: 'admin.mobile-app.mobile-app', + type: 'link', + path: '/settings/mobile-app', + icon: 'smartphone' + } + ], + [ + MenuId.security_settings, + { + id: MenuId.security_settings, + name: 'security.security', + type: 'toggle', + path: '/security-settings', + icon: 'security' + } + ], + [ + MenuId.security_settings_general, + { + id: MenuId.security_settings_general, + name: 'admin.general', + fullName: 'security.general-settings', + type: 'link', + path: '/security-settings/general', + icon: 'settings_applications' + } + ], + [ + MenuId.two_fa, + { + id: MenuId.two_fa, + name: 'admin.2fa.2fa', + type: 'link', + path: '/security-settings/2fa', + icon: 'mdi:two-factor-authentication' + } + ], + [ + MenuId.oauth2, + { + id: MenuId.oauth2, + name: 'admin.oauth2.oauth2', + type: 'link', + path: '/security-settings/oauth2', + icon: 'mdi:shield-account' + } + ], + [ + MenuId.audit_log, + { + id: MenuId.audit_log, + name: 'audit-log.audit-logs', + type: 'link', + path: '/security-settings/auditLogs', + icon: 'track_changes' + } + ], + [ + MenuId.alarms, + { + id: MenuId.alarms, + name: 'alarm.alarms', + type: 'link', + path: '/alarms', + icon: 'mdi:alert-outline' + } + ], + [ + MenuId.dashboards, + { + id: MenuId.dashboards, + name: 'dashboard.dashboards', + type: 'link', + path: '/dashboards', + icon: 'dashboards' + } + ], + [ + MenuId.entities, + { + id: MenuId.entities, + name: 'entity.entities', + type: 'toggle', + path: '/entities', + icon: 'category' + } + ], + [ + MenuId.devices, + { + id: MenuId.devices, + name: 'device.devices', + type: 'link', + path: '/entities/devices', + icon: 'devices_other' + } + ], + [ + MenuId.assets, + { + id: MenuId.assets, + name: 'asset.assets', + type: 'link', + path: '/entities/assets', + icon: 'domain' + } + ], + [ + MenuId.entity_views, + { + id: MenuId.entity_views, + name: 'entity-view.entity-views', + type: 'link', + path: '/entities/entityViews', + icon: 'view_quilt' + } + ], + [ + MenuId.profiles, + { + id: MenuId.profiles, + name: 'profiles.profiles', + type: 'toggle', + path: '/profiles', + icon: 'badge' + } + ], + [ + MenuId.device_profiles, + { + id: MenuId.device_profiles, + name: 'device-profile.device-profiles', + type: 'link', + path: '/profiles/deviceProfiles', + icon: 'mdi:alpha-d-box' + } + ], + [ + MenuId.asset_profiles, + { + id: MenuId.asset_profiles, + name: 'asset-profile.asset-profiles', + type: 'link', + path: '/profiles/assetProfiles', + icon: 'mdi:alpha-a-box' + } + ], + [ + MenuId.customers, + { + id: MenuId.customers, + name: 'customer.customers', + type: 'link', + path: '/customers', + icon: 'supervisor_account' + } + ], + [ + MenuId.rule_chains, + { + id: MenuId.rule_chains, + name: 'rulechain.rulechains', + type: 'link', + path: '/ruleChains', + icon: 'settings_ethernet' + } + ], + [ + MenuId.edge_management, + { + id: MenuId.edge_management, + name: 'edge.management', + type: 'toggle', + path: '/edgeManagement', + icon: 'settings_input_antenna' + } + ], + [ + MenuId.edges, + { + id: MenuId.edges, + name: 'edge.instances', + fullName: 'edge.edge-instances', + type: 'link', + path: '/edgeManagement/instances', + icon: 'router' + } + ], + [ + MenuId.rulechain_templates, + { + id: MenuId.rulechain_templates, + name: 'edge.rulechain-templates', + fullName: 'edge.edge-rulechain-templates', + type: 'link', + path: '/edgeManagement/ruleChains', + icon: 'settings_ethernet' + } + ], + [ + MenuId.features, + { + id: MenuId.features, + name: 'feature.advanced-features', + type: 'toggle', + path: '/features', + icon: 'construction' + } + ], + [ + MenuId.otaUpdates, + { + id: MenuId.otaUpdates, + name: 'ota-update.ota-updates', + type: 'link', + path: '/features/otaUpdates', + icon: 'memory' + } + ], + [ + MenuId.version_control, + { + id: MenuId.version_control, + name: 'version-control.version-control', + type: 'link', + path: '/features/vc', + icon: 'history' + } + ], + [ + MenuId.api_usage, + { + id: MenuId.api_usage, + name: 'api-usage.api-usage', + type: 'link', + path: '/usage', + icon: 'insert_chart' + } + ] +]); + +const menuFilters = new Map([ + [ + MenuId.edges, (authState) => authState.edgesSupportEnabled + ], + [ + MenuId.edge_management, (authState) => authState.edgesSupportEnabled + ], + [ + MenuId.rulechain_templates, (authState) => authState.edgesSupportEnabled + ] +]); + +const defaultUserMenuMap = new Map([ + [ + Authority.SYS_ADMIN, + [ + {id: MenuId.home}, + {id: MenuId.tenants}, + {id: MenuId.tenant_profiles}, + { + id: MenuId.resources, + pages: [ + { + id: MenuId.widget_library, + pages: [ + {id: MenuId.widget_types}, + {id: MenuId.widgets_bundles} + ] + }, + {id: MenuId.images}, + {id: MenuId.scada_symbols}, + {id: MenuId.resources_library} + ] + }, + { + id: MenuId.notifications_center, + pages: [ + {id: MenuId.notification_inbox}, + {id: MenuId.notification_sent}, + {id: MenuId.notification_recipients}, + {id: MenuId.notification_templates}, + {id: MenuId.notification_rules} + ] + }, + { + id: MenuId.settings, + pages: [ + {id: MenuId.general}, + {id: MenuId.mail_server}, + {id: MenuId.notification_settings}, + {id: MenuId.queues}, + {id: MenuId.mobile_app_settings} + ] + }, + { + id: MenuId.security_settings, + pages: [ + {id: MenuId.security_settings_general}, + {id: MenuId.two_fa}, + {id: MenuId.oauth2} + ] + } + ] + ], + [ + Authority.TENANT_ADMIN, + [ + {id: MenuId.home}, + {id: MenuId.alarms}, + {id: MenuId.dashboards}, + { + id: MenuId.entities, + pages: [ + {id: MenuId.devices}, + {id: MenuId.assets}, + {id: MenuId.entity_views} + ] + }, + { + id: MenuId.profiles, + pages: [ + {id: MenuId.device_profiles}, + {id: MenuId.asset_profiles} + ] + }, + {id: MenuId.customers}, + {id: MenuId.rule_chains}, + { + id: MenuId.edge_management, + pages: [ + {id: MenuId.edges}, + {id: MenuId.rulechain_templates} + ] + }, + { + id: MenuId.features, + pages: [ + {id: MenuId.otaUpdates}, + {id: MenuId.version_control} + ] + }, + { + id: MenuId.resources, + pages: [ + { + id: MenuId.widget_library, + pages: [ + {id: MenuId.widget_types}, + {id: MenuId.widgets_bundles} + ] + }, + {id: MenuId.images}, + {id: MenuId.scada_symbols}, + {id: MenuId.resources_library} + ] + }, + { + id: MenuId.notifications_center, + pages: [ + {id: MenuId.notification_inbox}, + {id: MenuId.notification_sent}, + {id: MenuId.notification_recipients}, + {id: MenuId.notification_templates}, + {id: MenuId.notification_rules} + ] + }, + {id: MenuId.api_usage}, + { + id: MenuId.settings, + pages: [ + {id: MenuId.home_settings}, + {id: MenuId.notification_settings}, + {id: MenuId.repository_settings}, + {id: MenuId.auto_commit_settings} + ] + }, + { + id: MenuId.security_settings, + pages: [ + {id: MenuId.audit_log} + ] + } + ] + ], + [ + Authority.CUSTOMER_USER, + [ + {id: MenuId.home}, + {id: MenuId.alarms}, + {id: MenuId.dashboards}, + { + id: MenuId.entities, + pages: [ + {id: MenuId.devices}, + {id: MenuId.assets}, + {id: MenuId.entity_views} + ] + }, + {id: MenuId.edges}, + { + id: MenuId.notifications_center, + pages: [ + {id: MenuId.notification_inbox} + ] + } + ] + ] +]); + +export const buildUserMenu = (authState: AuthState): Array => { + const references = defaultUserMenuMap.get(authState.authUser.authority); + return (references || []).map(ref => referenceToMenuSection(authState, ref)).filter(section => !!section); +}; + +const referenceToMenuSection = (authState: AuthState, reference: MenuReference): MenuSection | undefined => { + if (filterMenuReference(authState, reference)) { + const section = menuSectionMap.get(reference.id); + if (section) { + const result = deepClone(section); + if (reference.pages?.length) { + result.pages = reference.pages.map(page => + referenceToMenuSection(authState, page)).filter(page => !!page); + } + return result; + } else { + return undefined; + } + } else { + return undefined; + } +}; + +const filterMenuReference = (authState: AuthState, reference: MenuReference): boolean => { + const filter = menuFilters.get(reference.id); + if (filter) { + if (filter(authState)) { + if (reference.pages?.length) { + if (reference.pages.every(page => !filterMenuReference(authState, page))) { + return false; + } + } + return true; + } + return false; + } else { + return true; + } +}; diff --git a/ui-ngx/src/app/core/services/menu.service.ts b/ui-ngx/src/app/core/services/menu.service.ts index 309d82ebc0..c51be16c5c 100644 --- a/ui-ngx/src/app/core/services/menu.service.ts +++ b/ui-ngx/src/app/core/services/menu.service.ts @@ -19,8 +19,8 @@ import { select, Store } from '@ngrx/store'; import { AppState } from '../core.state'; import { getCurrentOpenedMenuSections, selectAuth, selectIsAuthenticated } from '../auth/auth.selectors'; import { filter, map, take } from 'rxjs/operators'; -import { HomeSection, MenuSection } from '@core/services/menu.models'; -import { BehaviorSubject, Observable, Subject } from 'rxjs'; +import { buildUserMenu, HomeSection, MenuId, MenuSection } from '@core/services/menu.models'; +import { BehaviorSubject, ReplaySubject, Observable, Subject } from 'rxjs'; import { Authority } from '@shared/models/authority.enum'; import { AuthState } from '@core/auth/auth.models'; import { NavigationEnd, Router } from '@angular/router'; @@ -31,11 +31,14 @@ import { NavigationEnd, Router } from '@angular/router'; export class MenuService { currentMenuSections: Array; - menuSections$: Subject> = new BehaviorSubject>([]); + menuSections$: Subject> = new ReplaySubject>(); homeSections$: Subject> = new BehaviorSubject>([]); availableMenuLinks$ = this.menuSections$.pipe( map((items) => this.allMenuLinks(items)) ); + availableMenuSections$ = this.menuSections$.pipe( + map((items) => this.allMenuSections(items)) + ); constructor(private store: Store, private router: Router) { @@ -60,18 +63,16 @@ export class MenuService { let homeSections: Array; switch (authState.authUser.authority) { case Authority.SYS_ADMIN: - this.currentMenuSections = this.buildSysAdminMenu(); homeSections = this.buildSysAdminHome(); break; case Authority.TENANT_ADMIN: - this.currentMenuSections = this.buildTenantAdminMenu(authState); homeSections = this.buildTenantAdminHome(authState); break; case Authority.CUSTOMER_USER: - this.currentMenuSections = this.buildCustomerUserMenu(authState); homeSections = this.buildCustomerUserHome(authState); break; } + this.currentMenuSections = buildUserMenu(authState); this.updateOpenedMenuSections(); this.menuSections$.next(this.currentMenuSections); this.homeSections$.next(homeSections); @@ -89,214 +90,6 @@ export class MenuService { ); } - private buildSysAdminMenu(): Array { - const sections: Array = []; - sections.push( - { - id: 'home', - name: 'home.home', - type: 'link', - path: '/home', - icon: 'home' - }, - { - id: 'tenants', - name: 'tenant.tenants', - type: 'link', - path: '/tenants', - icon: 'supervisor_account' - }, - { - id: 'tenant_profiles', - name: 'tenant-profile.tenant-profiles', - type: 'link', - path: '/tenantProfiles', - icon: 'mdi:alpha-t-box' - }, - { - id: 'resources', - name: 'admin.resources', - type: 'toggle', - path: '/resources', - icon: 'folder', - pages: [ - { - id: 'widget_library', - name: 'widget.widget-library', - type: 'link', - path: '/resources/widgets-library', - icon: 'now_widgets', - pages: [ - { - id: 'widget_types', - name: 'widget.widgets', - type: 'link', - path: '/resources/widgets-library/widget-types', - icon: 'now_widgets' - }, - { - id: 'widgets_bundles', - name: 'widgets-bundle.widgets-bundles', - type: 'link', - path: '/resources/widgets-library/widgets-bundles', - icon: 'now_widgets' - } - ] - }, - { - id: 'images', - name: 'image.gallery', - type: 'link', - path: '/resources/images', - icon: 'filter' - }, - { - id: 'scada_symbols', - name: 'scada.symbols', - type: 'link', - path: '/resources/scada-symbols', - icon: 'view_in_ar' - }, - { - id: 'resources_library', - name: 'resource.resources-library', - type: 'link', - path: '/resources/resources-library', - icon: 'mdi:rhombus-split' - } - ] - }, - { - id: 'notifications_center', - name: 'notification.notification-center', - type: 'link', - path: '/notification', - icon: 'mdi:message-badge', - pages: [ - { - id: 'notification_inbox', - name: 'notification.inbox', - fullName: 'notification.notification-inbox', - type: 'link', - path: '/notification/inbox', - icon: 'inbox' - }, - { - id: 'notification_sent', - name: 'notification.sent', - fullName: 'notification.notification-sent', - type: 'link', - path: '/notification/sent', - icon: 'outbox' - }, - { - id: 'notification_recipients', - name: 'notification.recipients', - fullName: 'notification.notification-recipients', - type: 'link', - path: '/notification/recipients', - icon: 'contacts' - }, - { - id: 'notification_templates', - name: 'notification.templates', - fullName: 'notification.notification-templates', - type: 'link', - path: '/notification/templates', - icon: 'mdi:message-draw' - }, - { - id: 'notification_rules', - name: 'notification.rules', - fullName: 'notification.notification-rules', - type: 'link', - path: '/notification/rules', - icon: 'mdi:message-cog' - } - ] - }, - { - id: 'settings', - name: 'admin.settings', - type: 'link', - path: '/settings', - icon: 'settings', - pages: [ - { - id: 'general', - name: 'admin.general', - fullName: 'admin.general-settings', - type: 'link', - path: '/settings/general', - icon: 'settings_applications' - }, - { - id: 'mail_server', - name: 'admin.outgoing-mail', - type: 'link', - path: '/settings/outgoing-mail', - icon: 'mail' - }, - { - id: 'notification_settings', - name: 'admin.notifications', - fullName: 'admin.notifications-settings', - type: 'link', - path: '/settings/notifications', - icon: 'mdi:message-badge' - }, - { - id: 'queues', - name: 'admin.queues', - type: 'link', - path: '/settings/queues', - icon: 'swap_calls' - }, - { - id: 'mobile_app_settings', - name: 'admin.mobile-app.mobile-app', - fullName: 'admin.mobile-app.mobile-app', - type: 'link', - path: '/settings/mobile-app', - icon: 'smartphone' - } - ] - }, - { - id: 'security_settings', - name: 'security.security', - type: 'toggle', - path: '/security-settings', - icon: 'security', - pages: [ - { - id: 'security_settings_general', - name: 'admin.general', - fullName: 'security.general-settings', - type: 'link', - path: '/security-settings/general', - icon: 'settings_applications' - }, - { - id: '2fa', - name: 'admin.2fa.2fa', - type: 'link', - path: '/security-settings/2fa', - icon: 'mdi:two-factor-authentication' - }, - { - id: 'oauth2', - name: 'admin.oauth2.oauth2', - type: 'link', - path: '/security-settings/oauth2', - icon: 'mdi:shield-account' - } - ] - } - ); - return sections; - } - private buildSysAdminHome(): Array { const homeSections: Array = []; homeSections.push( @@ -374,321 +167,6 @@ export class MenuService { return homeSections; } - private buildTenantAdminMenu(authState: AuthState): Array { - const sections: Array = []; - sections.push( - { - id: 'home', - name: 'home.home', - type: 'link', - path: '/home', - icon: 'home' - }, - { - id: 'alarms', - name: 'alarm.alarms', - type: 'link', - path: '/alarms', - icon: 'mdi:alert-outline' - }, - { - id: 'dashboards', - name: 'dashboard.dashboards', - type: 'link', - path: '/dashboards', - icon: 'dashboards' - }, - { - id: 'entities', - name: 'entity.entities', - type: 'toggle', - path: '/entities', - icon: 'category', - pages: [ - { - id: 'devices', - name: 'device.devices', - type: 'link', - path: '/entities/devices', - icon: 'devices_other' - }, - { - id: 'assets', - name: 'asset.assets', - type: 'link', - path: '/entities/assets', - icon: 'domain' - }, - { - id: 'entity_views', - name: 'entity-view.entity-views', - type: 'link', - path: '/entities/entityViews', - icon: 'view_quilt' - } - ] - }, - { - id: 'profiles', - name: 'profiles.profiles', - type: 'toggle', - path: '/profiles', - icon: 'badge', - pages: [ - { - id: 'device_profiles', - name: 'device-profile.device-profiles', - type: 'link', - path: '/profiles/deviceProfiles', - icon: 'mdi:alpha-d-box' - }, - { - id: 'asset_profiles', - name: 'asset-profile.asset-profiles', - type: 'link', - path: '/profiles/assetProfiles', - icon: 'mdi:alpha-a-box' - } - ] - }, - { - id: 'customers', - name: 'customer.customers', - type: 'link', - path: '/customers', - icon: 'supervisor_account' - }, - { - id: 'rule_chains', - name: 'rulechain.rulechains', - type: 'link', - path: '/ruleChains', - icon: 'settings_ethernet' - } - ); - if (authState.edgesSupportEnabled) { - sections.push( - { - id: 'edge_management', - name: 'edge.management', - type: 'toggle', - path: '/edgeManagement', - icon: 'settings_input_antenna', - pages: [ - { - id: 'edges', - name: 'edge.instances', - fullName: 'edge.edge-instances', - type: 'link', - path: '/edgeManagement/instances', - icon: 'router' - }, - { - id: 'rulechain_templates', - name: 'edge.rulechain-templates', - fullName: 'edge.edge-rulechain-templates', - type: 'link', - path: '/edgeManagement/ruleChains', - icon: 'settings_ethernet' - } - ] - } - ); - } - sections.push( - { - id: 'features', - name: 'feature.advanced-features', - type: 'toggle', - path: '/features', - icon: 'construction', - pages: [ - { - id: 'otaUpdates', - name: 'ota-update.ota-updates', - type: 'link', - path: '/features/otaUpdates', - icon: 'memory' - }, - { - id: 'version_control', - name: 'version-control.version-control', - type: 'link', - path: '/features/vc', - icon: 'history' - } - ] - }, - { - id: 'resources', - name: 'admin.resources', - type: 'toggle', - path: '/resources', - icon: 'folder', - pages: [ - { - id: 'widget_library', - name: 'widget.widget-library', - type: 'link', - path: '/resources/widgets-library', - icon: 'now_widgets', - pages: [ - { - id: 'widget_types', - name: 'widget.widgets', - type: 'link', - path: '/resources/widgets-library/widget-types', - icon: 'now_widgets' - }, - { - id: 'widgets_bundles', - name: 'widgets-bundle.widgets-bundles', - type: 'link', - path: '/resources/widgets-library/widgets-bundles', - icon: 'now_widgets' - } - ] - }, - { - id: 'images', - name: 'image.gallery', - type: 'link', - path: '/resources/images', - icon: 'filter' - }, - { - id: 'scada_symbols', - name: 'scada.symbols', - type: 'link', - path: '/resources/scada-symbols', - icon: 'view_in_ar' - }, - { - id: 'resources_library', - name: 'resource.resources-library', - type: 'link', - path: '/resources/resources-library', - icon: 'mdi:rhombus-split' - } - ] - }, - { - id: 'notifications_center', - name: 'notification.notification-center', - type: 'link', - path: '/notification', - icon: 'mdi:message-badge', - pages: [ - { - id: 'notification_inbox', - name: 'notification.inbox', - fullName: 'notification.notification-inbox', - type: 'link', - path: '/notification/inbox', - icon: 'inbox' - }, - { - id: 'notification_sent', - name: 'notification.sent', - fullName: 'notification.notification-sent', - type: 'link', - path: '/notification/sent', - icon: 'outbox' - }, - { - id: 'notification_recipients', - name: 'notification.recipients', - fullName: 'notification.notification-recipients', - type: 'link', - path: '/notification/recipients', - icon: 'contacts' - }, - { - id: 'notification_templates', - name: 'notification.templates', - fullName: 'notification.notification-templates', - type: 'link', - path: '/notification/templates', - icon: 'mdi:message-draw' - }, - { - id: 'notification_rules', - name: 'notification.rules', - fullName: 'notification.notification-rules', - type: 'link', - path: '/notification/rules', - icon: 'mdi:message-cog' - } - ] - }, - { - id: 'api_usage', - name: 'api-usage.api-usage', - type: 'link', - path: '/usage', - icon: 'insert_chart' - }, - { - id: 'settings', - name: 'admin.settings', - type: 'link', - path: '/settings', - icon: 'settings', - pages: [ - { - id: 'home_settings', - name: 'admin.home', - fullName: 'admin.home-settings', - type: 'link', - path: '/settings/home', - icon: 'settings_applications' - }, - { - id: 'notification_settings', - name: 'admin.notifications', - fullName: 'admin.notifications-settings', - type: 'link', - path: '/settings/notifications', - icon: 'mdi:message-badge' - }, - { - id: 'repository_settings', - name: 'admin.repository', - fullName: 'admin.repository-settings', - type: 'link', - path: '/settings/repository', - icon: 'manage_history' - }, - { - id: 'auto_commit_settings', - name: 'admin.auto-commit', - fullName: 'admin.auto-commit-settings', - type: 'link', - path: '/settings/auto-commit', - icon: 'settings_backup_restore' - } - ] - }, - { - id: 'security_settings', - name: 'security.security', - type: 'toggle', - path: '/security-settings', - icon: 'security', - pages: [ - { - id: 'audit_log', - name: 'audit-log.audit-logs', - type: 'link', - path: '/security-settings/auditLogs', - icon: 'track_changes' - } - ] - } - ); - return sections; - } - private buildTenantAdminHome(authState: AuthState): Array { const homeSections: Array = []; homeSections.push( @@ -847,95 +325,6 @@ export class MenuService { return homeSections; } - private buildCustomerUserMenu(authState: AuthState): Array { - const sections: Array = []; - sections.push( - { - id: 'home', - name: 'home.home', - type: 'link', - path: '/home', - icon: 'home' - }, - { - id: 'alarms', - name: 'alarm.alarms', - type: 'link', - path: '/alarms', - icon: 'mdi:alert-outline' - }, - { - id: 'dashboards', - name: 'dashboard.dashboards', - type: 'link', - path: '/dashboards', - icon: 'dashboards' - }, - { - id: 'entities', - name: 'entity.entities', - type: 'toggle', - path: '/entities', - icon: 'category', - pages: [ - { - id: 'devices', - name: 'device.devices', - type: 'link', - path: '/entities/devices', - icon: 'devices_other' - }, - { - id: 'assets', - name: 'asset.assets', - type: 'link', - path: '/entities/assets', - icon: 'domain' - }, - { - id: 'entity_views', - name: 'entity-view.entity-views', - type: 'link', - path: '/entities/entityViews', - icon: 'view_quilt' - } - ] - } - ); - if (authState.edgesSupportEnabled) { - sections.push( - { - id: 'edges', - name: 'edge.edge-instances', - fullName: 'edge.edge-instances', - type: 'link', - path: '/edgeManagement/instances', - icon: 'router' - } - ); - } - sections.push( - { - id: 'notifications_center', - name: 'notification.notification-center', - type: 'link', - path: '/notification', - icon: 'mdi:message-badge', - pages: [ - { - id: 'notification_inbox', - name: 'notification.inbox', - fullName: 'notification.notification-inbox', - type: 'link', - path: '/notification/inbox', - icon: 'inbox' - } - ] - } - ); - return sections; - } - private buildCustomerUserHome(authState: AuthState): Array { const homeSections: Array = []; homeSections.push( @@ -1012,6 +401,17 @@ export class MenuService { return result; } + private allMenuSections(sections: Array): Array { + const result: Array = []; + for (const section of sections) { + result.push(section); + if (section.pages && section.pages.length) { + result.push(...this.allMenuSections(section.pages)); + } + } + return result; + } + public menuSections(): Observable> { return this.menuSections$; } @@ -1024,7 +424,11 @@ export class MenuService { return this.availableMenuLinks$; } - public menuLinkById(id: string): Observable { + public availableMenuSections(): Observable> { + return this.availableMenuSections$; + } + + public menuLinkById(id: MenuId | string): Observable { return this.availableMenuLinks$.pipe( map((links) => links.find(link => link.id === id)) ); diff --git a/ui-ngx/src/app/modules/home/pages/admin/admin-routing.module.ts b/ui-ngx/src/app/modules/home/pages/admin/admin-routing.module.ts index 1c4d6c204d..a01826bd08 100644 --- a/ui-ngx/src/app/modules/home/pages/admin/admin-routing.module.ts +++ b/ui-ngx/src/app/modules/home/pages/admin/admin-routing.module.ts @@ -45,6 +45,7 @@ import { ImageResourceType, IMAGES_URL_PREFIX, ResourceSubType } from '@shared/m import { ScadaSymbolComponent } from '@home/pages/scada-symbol/scada-symbol.component'; import { ImageService } from '@core/http/image.service'; import { ScadaSymbolData } from '@home/pages/scada-symbol/scada-symbol-editor.models'; +import { MenuId } from '@core/services/menu.models'; @Injectable() export class OAuth2LoginProcessingUrlResolver implements Resolve { @@ -79,8 +80,7 @@ const routes: Routes = [ data: { auth: [Authority.SYS_ADMIN, Authority.TENANT_ADMIN], breadcrumb: { - label: 'admin.resources', - icon: 'folder' + menuId: MenuId.resources } }, children: [ @@ -97,8 +97,7 @@ const routes: Routes = [ path: 'images', data: { breadcrumb: { - label: 'image.gallery', - icon: 'filter' + menuId: MenuId.images } }, children: [ @@ -117,8 +116,7 @@ const routes: Routes = [ path: 'scada-symbols', data: { breadcrumb: { - label: 'scada.symbols', - icon: 'view_in_ar' + menuId: MenuId.scada_symbols } }, children: [ @@ -153,8 +151,7 @@ const routes: Routes = [ path: 'resources-library', data: { breadcrumb: { - label: 'resource.resources-library', - icon: 'mdi:rhombus-split' + menuId: MenuId.resources_library } }, children: [ @@ -196,8 +193,7 @@ const routes: Routes = [ auth: [Authority.SYS_ADMIN, Authority.TENANT_ADMIN], showMainLoadingBar: false, breadcrumb: { - label: 'admin.settings', - icon: 'settings' + menuId: MenuId.settings } }, children: [ @@ -220,8 +216,7 @@ const routes: Routes = [ auth: [Authority.SYS_ADMIN], title: 'admin.general-settings', breadcrumb: { - label: 'admin.general', - icon: 'settings_applications' + menuId: MenuId.general } } }, @@ -233,8 +228,7 @@ const routes: Routes = [ auth: [Authority.SYS_ADMIN], title: 'admin.outgoing-mail-settings', breadcrumb: { - label: 'admin.outgoing-mail', - icon: 'mail' + menuId: MenuId.mail_server } } }, @@ -246,8 +240,7 @@ const routes: Routes = [ auth: [Authority.SYS_ADMIN, Authority.TENANT_ADMIN], title: 'admin.notifications-settings', breadcrumb: { - label: 'admin.notifications', - icon: 'mdi:message-badge' + menuId: MenuId.notification_settings } } }, @@ -255,8 +248,7 @@ const routes: Routes = [ path: 'queues', data: { breadcrumb: { - label: 'admin.queues', - icon: 'swap_calls' + menuId: MenuId.queues } }, children: [ @@ -297,8 +289,7 @@ const routes: Routes = [ auth: [Authority.TENANT_ADMIN], title: 'admin.home-settings', breadcrumb: { - label: 'admin.home', - icon: 'settings_applications' + menuId: MenuId.home_settings } } }, @@ -310,8 +301,7 @@ const routes: Routes = [ auth: [Authority.TENANT_ADMIN], title: 'admin.repository-settings', breadcrumb: { - label: 'admin.repository', - icon: 'manage_history' + menuId: MenuId.repository_settings } } }, @@ -323,8 +313,7 @@ const routes: Routes = [ auth: [Authority.TENANT_ADMIN], title: 'admin.auto-commit-settings', breadcrumb: { - label: 'admin.auto-commit', - icon: 'settings_backup_restore' + menuId: MenuId.auto_commit_settings } } }, @@ -336,8 +325,7 @@ const routes: Routes = [ auth: [Authority.SYS_ADMIN], title: 'admin.mobile-app.mobile-app', breadcrumb: { - label: 'admin.mobile-app.mobile-app', - icon: 'smartphone' + menuId: MenuId.mobile_app_settings } } }, @@ -373,8 +361,7 @@ const routes: Routes = [ data: { auth: [Authority.SYS_ADMIN, Authority.TENANT_ADMIN], breadcrumb: { - label: 'security.security', - icon: 'security' + menuId: MenuId.security_settings } }, children: [ @@ -397,8 +384,7 @@ const routes: Routes = [ auth: [Authority.SYS_ADMIN], title: 'admin.general', breadcrumb: { - label: 'admin.general', - icon: 'settings_applications' + menuId: MenuId.security_settings_general } } }, @@ -410,8 +396,7 @@ const routes: Routes = [ auth: [Authority.SYS_ADMIN], title: 'admin.2fa.2fa', breadcrumb: { - label: 'admin.2fa.2fa', - icon: 'mdi:two-factor-authentication' + menuId: MenuId.two_fa } } }, @@ -423,8 +408,7 @@ const routes: Routes = [ auth: [Authority.SYS_ADMIN], title: 'admin.oauth2.oauth2', breadcrumb: { - label: 'admin.oauth2.oauth2', - icon: 'mdi:shield-account' + menuId: MenuId.oauth2 } }, resolve: { diff --git a/ui-ngx/src/app/modules/home/pages/alarm/alarm-routing.module.ts b/ui-ngx/src/app/modules/home/pages/alarm/alarm-routing.module.ts index 0f971a879d..3ada68643f 100644 --- a/ui-ngx/src/app/modules/home/pages/alarm/alarm-routing.module.ts +++ b/ui-ngx/src/app/modules/home/pages/alarm/alarm-routing.module.ts @@ -21,6 +21,7 @@ import { Observable } from 'rxjs'; import { OAuth2Service } from '@core/http/oauth2.service'; import { AlarmTableComponent } from '@home/components/alarm/alarm-table.component'; import { AlarmsMode } from '@shared/models/alarm.models'; +import { MenuId } from '@core/services/menu.models'; @Injectable() export class OAuth2LoginProcessingUrlResolver implements Resolve { @@ -41,8 +42,7 @@ const routes: Routes = [ auth: [Authority.TENANT_ADMIN, Authority.CUSTOMER_USER], title: 'alarm.alarms', breadcrumb: { - label: 'alarm.alarms', - icon: 'mdi:alert-outline' + menuId: MenuId.alarms }, isPage: true, alarmsMode: AlarmsMode.ALL diff --git a/ui-ngx/src/app/modules/home/pages/api-usage/api-usage-routing.module.ts b/ui-ngx/src/app/modules/home/pages/api-usage/api-usage-routing.module.ts index bc582da666..f76ceb86bc 100644 --- a/ui-ngx/src/app/modules/home/pages/api-usage/api-usage-routing.module.ts +++ b/ui-ngx/src/app/modules/home/pages/api-usage/api-usage-routing.module.ts @@ -21,6 +21,7 @@ import { ApiUsageComponent } from '@home/pages/api-usage/api-usage.component'; import { Dashboard } from '@shared/models/dashboard.models'; import { ResourcesService } from '@core/services/resources.service'; import { Observable } from 'rxjs'; +import { MenuId } from '@core/services/menu.models'; const apiUsageDashboardJson = '/assets/dashboard/api_usage.json'; @@ -38,8 +39,7 @@ const routes: Routes = [ auth: [Authority.TENANT_ADMIN], title: 'api-usage.api-usage', breadcrumb: { - label: 'api-usage.api-usage', - icon: 'insert_chart' + menuId: MenuId.api_usage } }, resolve: { diff --git a/ui-ngx/src/app/modules/home/pages/asset-profile/asset-profile-routing.module.ts b/ui-ngx/src/app/modules/home/pages/asset-profile/asset-profile-routing.module.ts index 6f7316b1ae..67229feace 100644 --- a/ui-ngx/src/app/modules/home/pages/asset-profile/asset-profile-routing.module.ts +++ b/ui-ngx/src/app/modules/home/pages/asset-profile/asset-profile-routing.module.ts @@ -24,14 +24,14 @@ import { ConfirmOnExitGuard } from '@core/guards/confirm-on-exit.guard'; import { entityDetailsPageBreadcrumbLabelFunction } from '@home/pages/home-pages.models'; import { BreadCrumbConfig } from '@shared/components/breadcrumb'; import { AssetProfilesTableConfigResolver } from './asset-profiles-table-config.resolver'; +import { MenuId } from '@core/services/menu.models'; export const assetProfilesRoutes: Routes = [ { path: 'assetProfiles', data: { breadcrumb: { - label: 'asset-profile.asset-profiles', - icon: 'mdi:alpha-a-box' + menuId: MenuId.asset_profiles } }, children: [ diff --git a/ui-ngx/src/app/modules/home/pages/asset/asset-routing.module.ts b/ui-ngx/src/app/modules/home/pages/asset/asset-routing.module.ts index 6ddc5735ab..05dec3b260 100644 --- a/ui-ngx/src/app/modules/home/pages/asset/asset-routing.module.ts +++ b/ui-ngx/src/app/modules/home/pages/asset/asset-routing.module.ts @@ -24,14 +24,14 @@ import { EntityDetailsPageComponent } from '@home/components/entity/entity-detai import { BreadCrumbConfig } from '@shared/components/breadcrumb'; import { ConfirmOnExitGuard } from '@core/guards/confirm-on-exit.guard'; import { entityDetailsPageBreadcrumbLabelFunction } from '@home/pages/home-pages.models'; +import { MenuId } from '@core/services/menu.models'; export const assetRoutes: Routes = [ { path: 'assets', data: { breadcrumb: { - label: 'asset.assets', - icon: 'domain' + menuId: MenuId.assets } }, children: [ diff --git a/ui-ngx/src/app/modules/home/pages/audit-log/audit-log-routing.module.ts b/ui-ngx/src/app/modules/home/pages/audit-log/audit-log-routing.module.ts index 768400a21e..5afeb2c694 100644 --- a/ui-ngx/src/app/modules/home/pages/audit-log/audit-log-routing.module.ts +++ b/ui-ngx/src/app/modules/home/pages/audit-log/audit-log-routing.module.ts @@ -18,6 +18,7 @@ import { NgModule } from '@angular/core'; import { RouterModule, Routes } from '@angular/router'; import { Authority } from '@shared/models/authority.enum'; import { AuditLogTableComponent } from '@home/components/audit-log/audit-log-table.component'; +import { MenuId } from '@core/services/menu.models'; export const auditLogsRoutes: Routes = [ { @@ -27,8 +28,7 @@ export const auditLogsRoutes: Routes = [ auth: [Authority.TENANT_ADMIN], title: 'audit-log.audit-logs', breadcrumb: { - label: 'audit-log.audit-logs', - icon: 'track_changes' + menuId: MenuId.audit_log }, isPage: true } diff --git a/ui-ngx/src/app/modules/home/pages/customer/customer-routing.module.ts b/ui-ngx/src/app/modules/home/pages/customer/customer-routing.module.ts index 7090ce7fc8..1339d55b40 100644 --- a/ui-ngx/src/app/modules/home/pages/customer/customer-routing.module.ts +++ b/ui-ngx/src/app/modules/home/pages/customer/customer-routing.module.ts @@ -31,14 +31,14 @@ import { EdgesTableConfigResolver } from '@home/pages/edge/edges-table-config.re import { EntityDetailsPageComponent } from '@home/components/entity/entity-details-page.component'; import { ConfirmOnExitGuard } from '@core/guards/confirm-on-exit.guard'; import { entityDetailsPageBreadcrumbLabelFunction } from '@home/pages/home-pages.models'; +import { MenuId } from '@core/services/menu.models'; const routes: Routes = [ { path: 'customers', data: { breadcrumb: { - label: 'customer.customers', - icon: 'supervisor_account' + menuId: MenuId.customers } }, children: [ diff --git a/ui-ngx/src/app/modules/home/pages/dashboard/dashboard-routing.module.ts b/ui-ngx/src/app/modules/home/pages/dashboard/dashboard-routing.module.ts index 223ce60825..28fc9d4e12 100644 --- a/ui-ngx/src/app/modules/home/pages/dashboard/dashboard-routing.module.ts +++ b/ui-ngx/src/app/modules/home/pages/dashboard/dashboard-routing.module.ts @@ -33,6 +33,7 @@ import { Store } from '@ngrx/store'; import { AppState } from '@core/core.state'; import { getCurrentAuthUser } from '@core/auth/auth.selectors'; import { ConfirmOnExitGuard } from '@core/guards/confirm-on-exit.guard'; +import { MenuId } from '@core/services/menu.models'; @Injectable() export class DashboardResolver implements Resolve { @@ -66,8 +67,7 @@ const routes: Routes = [ path: 'dashboards', data: { breadcrumb: { - label: 'dashboard.dashboards', - icon: 'dashboard' + menuId: MenuId.dashboards } }, children: [ diff --git a/ui-ngx/src/app/modules/home/pages/device-profile/device-profile-routing.module.ts b/ui-ngx/src/app/modules/home/pages/device-profile/device-profile-routing.module.ts index 9626d8a616..66b7961914 100644 --- a/ui-ngx/src/app/modules/home/pages/device-profile/device-profile-routing.module.ts +++ b/ui-ngx/src/app/modules/home/pages/device-profile/device-profile-routing.module.ts @@ -24,14 +24,14 @@ import { EntityDetailsPageComponent } from '@home/components/entity/entity-detai import { ConfirmOnExitGuard } from '@core/guards/confirm-on-exit.guard'; import { entityDetailsPageBreadcrumbLabelFunction } from '@home/pages/home-pages.models'; import { BreadCrumbConfig } from '@shared/components/breadcrumb'; +import { MenuId } from '@core/services/menu.models'; export const deviceProfilesRoutes: Routes = [ { path: 'deviceProfiles', data: { breadcrumb: { - label: 'device-profile.device-profiles', - icon: 'mdi:alpha-d-box' + menuId: MenuId.device_profiles } }, children: [ diff --git a/ui-ngx/src/app/modules/home/pages/device/device-routing.module.ts b/ui-ngx/src/app/modules/home/pages/device/device-routing.module.ts index 5fa15dd375..01b4370e55 100644 --- a/ui-ngx/src/app/modules/home/pages/device/device-routing.module.ts +++ b/ui-ngx/src/app/modules/home/pages/device/device-routing.module.ts @@ -26,14 +26,14 @@ import { entityDetailsPageBreadcrumbLabelFunction } from '@home/pages/home-pages import { BreadCrumbConfig } from '@shared/components/breadcrumb'; import { assetRoutes } from '@home/pages/asset/asset-routing.module'; import { entityViewRoutes } from '@home/pages/entity-view/entity-view-routing.module'; +import { MenuId } from '@core/services/menu.models'; export const deviceRoutes: Routes = [ { path: 'devices', data: { breadcrumb: { - label: 'device.devices', - icon: 'devices_other' + menuId: MenuId.devices } }, children: [ diff --git a/ui-ngx/src/app/modules/home/pages/edge/edge-routing.module.ts b/ui-ngx/src/app/modules/home/pages/edge/edge-routing.module.ts index 484bb5336b..1302bfa140 100644 --- a/ui-ngx/src/app/modules/home/pages/edge/edge-routing.module.ts +++ b/ui-ngx/src/app/modules/home/pages/edge/edge-routing.module.ts @@ -41,14 +41,14 @@ import { } from '@home/pages/rulechain/rulechain-routing.module'; import { EntityDetailsPageComponent } from '@home/components/entity/entity-details-page.component'; import { entityDetailsPageBreadcrumbLabelFunction } from '@home/pages/home-pages.models'; +import { MenuId } from '@core/services/menu.models'; const routes: Routes = [ { path: 'edgeManagement', data: { breadcrumb: { - label: 'edge.management', - icon: 'settings_input_antenna' + menuId: MenuId.edge_management } }, children: [ @@ -64,8 +64,7 @@ const routes: Routes = [ path: 'instances', data: { breadcrumb: { - label: 'edge.instances', - icon: 'router' + menuId: MenuId.edges } }, children: [ @@ -307,8 +306,7 @@ const routes: Routes = [ path: 'ruleChains', data: { breadcrumb: { - label: 'edge.rulechain-templates', - icon: 'settings_ethernet' + menuId: MenuId.rulechain_templates } }, children: [ diff --git a/ui-ngx/src/app/modules/home/pages/entity-view/entity-view-routing.module.ts b/ui-ngx/src/app/modules/home/pages/entity-view/entity-view-routing.module.ts index d08f48bf7b..4666b7234f 100644 --- a/ui-ngx/src/app/modules/home/pages/entity-view/entity-view-routing.module.ts +++ b/ui-ngx/src/app/modules/home/pages/entity-view/entity-view-routing.module.ts @@ -24,14 +24,14 @@ import { EntityDetailsPageComponent } from '@home/components/entity/entity-detai import { ConfirmOnExitGuard } from '@core/guards/confirm-on-exit.guard'; import { entityDetailsPageBreadcrumbLabelFunction } from '@home/pages/home-pages.models'; import { BreadCrumbConfig } from '@shared/components/breadcrumb'; +import { MenuId } from '@core/services/menu.models'; export const entityViewRoutes: Routes = [ { path: 'entityViews', data: { breadcrumb: { - label: 'entity-view.entity-views', - icon: 'view_quilt' + menuId: MenuId.entity_views } }, children: [ diff --git a/ui-ngx/src/app/modules/home/pages/features/features-routing.module.ts b/ui-ngx/src/app/modules/home/pages/features/features-routing.module.ts index 7be7e896e8..0fc21f90f7 100644 --- a/ui-ngx/src/app/modules/home/pages/features/features-routing.module.ts +++ b/ui-ngx/src/app/modules/home/pages/features/features-routing.module.ts @@ -19,6 +19,7 @@ import { Authority } from '@shared/models/authority.enum'; import { NgModule } from '@angular/core'; import { otaUpdatesRoutes } from '@home/pages/ota-update/ota-update-routing.module'; import { vcRoutes } from '@home/pages/vc/vc-routing.module'; +import { MenuId } from '@core/services/menu.models'; const routes: Routes = [ { @@ -26,8 +27,7 @@ const routes: Routes = [ data: { auth: [Authority.TENANT_ADMIN], breadcrumb: { - label: 'feature.advanced-features', - icon: 'construction' + menuId: MenuId.features } }, children: [ diff --git a/ui-ngx/src/app/modules/home/pages/home-links/home-links-routing.module.ts b/ui-ngx/src/app/modules/home/pages/home-links/home-links-routing.module.ts index 303a1bce4e..153fba8c93 100644 --- a/ui-ngx/src/app/modules/home/pages/home-links/home-links-routing.module.ts +++ b/ui-ngx/src/app/modules/home/pages/home-links/home-links-routing.module.ts @@ -34,6 +34,7 @@ import { import { EntityKeyType } from '@shared/models/query/query.models'; import { ResourcesService } from '@core/services/resources.service'; import { isDefinedAndNotNull } from '@core/utils'; +import { MenuId } from '@core/services/menu.models'; const sysAdminHomePageJson = '/assets/dashboard/sys_admin_home_page.json'; const tenantAdminHomePageJson = '/assets/dashboard/tenant_admin_home_page.json'; @@ -125,8 +126,7 @@ const routes: Routes = [ auth: [Authority.SYS_ADMIN, Authority.TENANT_ADMIN, Authority.CUSTOMER_USER], title: 'home.home', breadcrumb: { - label: 'home.home', - icon: 'home' + menuId: MenuId.home } }, resolve: { diff --git a/ui-ngx/src/app/modules/home/pages/notification/notification-routing.module.ts b/ui-ngx/src/app/modules/home/pages/notification/notification-routing.module.ts index 41f49ee708..b1bf35bcc6 100644 --- a/ui-ngx/src/app/modules/home/pages/notification/notification-routing.module.ts +++ b/ui-ngx/src/app/modules/home/pages/notification/notification-routing.module.ts @@ -25,6 +25,7 @@ import { RecipientTableConfigResolver } from '@home/pages/notification/recipient import { TemplateTableConfigResolver } from '@home/pages/notification/template/template-table-config.resolver'; import { RuleTableConfigResolver } from '@home/pages/notification/rule/rule-table-config.resolver'; import { SendNotificationButtonComponent } from '@home/components/notification/send-notification-button.component'; +import { MenuId } from '@core/services/menu.models'; const routes: Routes = [ { @@ -33,8 +34,7 @@ const routes: Routes = [ data: { auth: [Authority.TENANT_ADMIN, Authority.CUSTOMER_USER, Authority.SYS_ADMIN], breadcrumb: { - label: 'notification.notification-center', - icon: 'mdi:message-badge' + menuId: MenuId.notifications_center }, routerTabsHeaderComponent: SendNotificationButtonComponent }, @@ -54,8 +54,7 @@ const routes: Routes = [ auth: [Authority.TENANT_ADMIN, Authority.CUSTOMER_USER, Authority.SYS_ADMIN], title: 'notification.inbox', breadcrumb: { - label: 'notification.inbox', - icon: 'inbox' + menuId: MenuId.notification_inbox } }, resolve: { @@ -69,8 +68,7 @@ const routes: Routes = [ auth: [Authority.TENANT_ADMIN, Authority.SYS_ADMIN], title: 'notification.sent', breadcrumb: { - label: 'notification.sent', - icon: 'outbox' + menuId: MenuId.notification_sent } }, resolve: { @@ -84,8 +82,7 @@ const routes: Routes = [ auth: [Authority.TENANT_ADMIN, Authority.SYS_ADMIN], title: 'notification.templates', breadcrumb: { - label: 'notification.templates', - icon: 'mdi:message-draw' + menuId: MenuId.notification_templates } }, resolve: { @@ -99,8 +96,7 @@ const routes: Routes = [ auth: [Authority.TENANT_ADMIN, Authority.SYS_ADMIN], title: 'notification.recipients', breadcrumb: { - label: 'notification.recipients', - icon: 'contacts' + menuId: MenuId.notification_recipients }, }, resolve: { @@ -114,8 +110,7 @@ const routes: Routes = [ auth: [Authority.TENANT_ADMIN, Authority.SYS_ADMIN], title: 'notification.rules', breadcrumb: { - label: 'notification.rules', - icon: 'mdi:message-cog' + menuId: MenuId.notification_rules } }, resolve: { diff --git a/ui-ngx/src/app/modules/home/pages/ota-update/ota-update-routing.module.ts b/ui-ngx/src/app/modules/home/pages/ota-update/ota-update-routing.module.ts index 78cb83bd9f..6904b72045 100644 --- a/ui-ngx/src/app/modules/home/pages/ota-update/ota-update-routing.module.ts +++ b/ui-ngx/src/app/modules/home/pages/ota-update/ota-update-routing.module.ts @@ -23,14 +23,14 @@ import { EntityDetailsPageComponent } from '@home/components/entity/entity-detai import { ConfirmOnExitGuard } from '@core/guards/confirm-on-exit.guard'; import { entityDetailsPageBreadcrumbLabelFunction } from '@home/pages/home-pages.models'; import { BreadCrumbConfig } from '@shared/components/breadcrumb'; +import { MenuId } from '@core/services/menu.models'; export const otaUpdatesRoutes: Routes = [ { path: 'otaUpdates', data: { breadcrumb: { - label: 'ota-update.ota-updates', - icon: 'memory' + menuId: MenuId.otaUpdates } }, children: [ diff --git a/ui-ngx/src/app/modules/home/pages/profiles/profiles-routing.module.ts b/ui-ngx/src/app/modules/home/pages/profiles/profiles-routing.module.ts index aa0ffdee0e..d64463064a 100644 --- a/ui-ngx/src/app/modules/home/pages/profiles/profiles-routing.module.ts +++ b/ui-ngx/src/app/modules/home/pages/profiles/profiles-routing.module.ts @@ -19,6 +19,7 @@ import { Authority } from '@shared/models/authority.enum'; import { NgModule } from '@angular/core'; import { deviceProfilesRoutes } from '@home/pages/device-profile/device-profile-routing.module'; import { assetProfilesRoutes } from '@home/pages/asset-profile/asset-profile-routing.module'; +import { MenuId } from '@core/services/menu.models'; const routes: Routes = [ { @@ -26,8 +27,7 @@ const routes: Routes = [ data: { auth: [Authority.TENANT_ADMIN], breadcrumb: { - label: 'profiles.profiles', - icon: 'badge' + menuId: MenuId.profiles } }, children: [ diff --git a/ui-ngx/src/app/modules/home/pages/rulechain/rulechain-routing.module.ts b/ui-ngx/src/app/modules/home/pages/rulechain/rulechain-routing.module.ts index 47fa4970c6..c341ae6848 100644 --- a/ui-ngx/src/app/modules/home/pages/rulechain/rulechain-routing.module.ts +++ b/ui-ngx/src/app/modules/home/pages/rulechain/rulechain-routing.module.ts @@ -39,7 +39,7 @@ import { RuleChainService } from '@core/http/rule-chain.service'; import { RuleChainPageComponent } from '@home/pages/rulechain/rulechain-page.component'; import { RuleNodeComponentDescriptor } from '@shared/models/rule-node.models'; import { ConfirmOnExitGuard } from '@core/guards/confirm-on-exit.guard'; -import { ItemBufferService } from '@core/public-api'; +import { ItemBufferService, MenuId } from '@core/public-api'; import { MODULES_MAP } from '@shared/public-api'; import { IModulesMap } from '@modules/common/modules-map.models'; @@ -127,8 +127,7 @@ const routes: Routes = [ path: 'ruleChains', data: { breadcrumb: { - label: 'rulechain.rulechains', - icon: 'settings_ethernet' + menuId: MenuId.rule_chains } }, children: [ diff --git a/ui-ngx/src/app/modules/home/pages/tenant-profile/tenant-profile-routing.module.ts b/ui-ngx/src/app/modules/home/pages/tenant-profile/tenant-profile-routing.module.ts index c414238733..a9fb7c961c 100644 --- a/ui-ngx/src/app/modules/home/pages/tenant-profile/tenant-profile-routing.module.ts +++ b/ui-ngx/src/app/modules/home/pages/tenant-profile/tenant-profile-routing.module.ts @@ -24,14 +24,14 @@ import { EntityDetailsPageComponent } from '@home/components/entity/entity-detai import { ConfirmOnExitGuard } from '@core/guards/confirm-on-exit.guard'; import { entityDetailsPageBreadcrumbLabelFunction } from '@home/pages/home-pages.models'; import { BreadCrumbConfig } from '@shared/components/breadcrumb'; +import { MenuId } from '@core/services/menu.models'; const routes: Routes = [ { path: 'tenantProfiles', data: { breadcrumb: { - label: 'tenant-profile.tenant-profiles', - icon: 'mdi:alpha-t-box' + menuId: MenuId.tenant_profiles } }, children: [ diff --git a/ui-ngx/src/app/modules/home/pages/tenant/tenant-routing.module.ts b/ui-ngx/src/app/modules/home/pages/tenant/tenant-routing.module.ts index b8ea897b66..141a799dce 100644 --- a/ui-ngx/src/app/modules/home/pages/tenant/tenant-routing.module.ts +++ b/ui-ngx/src/app/modules/home/pages/tenant/tenant-routing.module.ts @@ -25,14 +25,14 @@ import { EntityDetailsPageComponent } from '@home/components/entity/entity-detai import { ConfirmOnExitGuard } from '@core/guards/confirm-on-exit.guard'; import { entityDetailsPageBreadcrumbLabelFunction } from '@home/pages/home-pages.models'; import { BreadCrumbConfig } from '@shared/components/breadcrumb'; +import { MenuId } from '@core/services/menu.models'; const routes: Routes = [ { path: 'tenants', data: { breadcrumb: { - label: 'tenant.tenants', - icon: 'supervisor_account' + menuId: MenuId.tenants } }, children: [ diff --git a/ui-ngx/src/app/modules/home/pages/vc/vc-routing.module.ts b/ui-ngx/src/app/modules/home/pages/vc/vc-routing.module.ts index 2708a42c11..1d4112021a 100644 --- a/ui-ngx/src/app/modules/home/pages/vc/vc-routing.module.ts +++ b/ui-ngx/src/app/modules/home/pages/vc/vc-routing.module.ts @@ -19,6 +19,7 @@ import { RouterModule, Routes } from '@angular/router'; import { ConfirmOnExitGuard } from '@core/guards/confirm-on-exit.guard'; import { Authority } from '@shared/models/authority.enum'; import { VersionControlComponent } from '@home/components/vc/version-control.component'; +import { MenuId } from '@core/services/menu.models'; export const vcRoutes: Routes = [ { @@ -29,8 +30,7 @@ export const vcRoutes: Routes = [ auth: [Authority.TENANT_ADMIN], title: 'version-control.version-control', breadcrumb: { - label: 'version-control.version-control', - icon: 'history' + menuId: MenuId.version_control } } } diff --git a/ui-ngx/src/app/modules/home/pages/widget/widget-library-routing.module.ts b/ui-ngx/src/app/modules/home/pages/widget/widget-library-routing.module.ts index c808bae163..046737ac10 100644 --- a/ui-ngx/src/app/modules/home/pages/widget/widget-library-routing.module.ts +++ b/ui-ngx/src/app/modules/home/pages/widget/widget-library-routing.module.ts @@ -33,6 +33,7 @@ import { WidgetTypesTableConfigResolver } from '@home/pages/widget/widget-types- import { WidgetsBundleWidgetsComponent } from '@home/pages/widget/widgets-bundle-widgets.component'; import { EntityDetailsPageComponent } from '@home/components/entity/entity-details-page.component'; import { entityDetailsPageBreadcrumbLabelFunction } from '@home/pages/home-pages.models'; +import { MenuId } from '@core/services/menu.models'; export interface WidgetEditorData { widgetTypeDetails: WidgetTypeDetails; @@ -108,8 +109,7 @@ const widgetTypesRoutes: Routes = [ path: 'widget-types', data: { breadcrumb: { - label: 'widget.widgets', - icon: 'now_widgets' + menuId: MenuId.widget_types } }, children: [ @@ -157,8 +157,7 @@ const widgetsBundlesRoutes: Routes = [ path: 'widgets-bundles', data: { breadcrumb: { - label: 'widgets-bundle.widgets-bundles', - icon: 'now_widgets' + menuId: MenuId.widgets_bundles } }, children: [ @@ -235,8 +234,7 @@ export const widgetsLibraryRoutes: Routes = [ data: { auth: [Authority.SYS_ADMIN, Authority.TENANT_ADMIN], breadcrumb: { - label: 'widget.widget-library', - icon: 'now_widgets' + menuId: MenuId.widget_library } }, children: [ diff --git a/ui-ngx/src/app/shared/components/breadcrumb.component.html b/ui-ngx/src/app/shared/components/breadcrumb.component.html index 6045f38ff2..506555d9e8 100644 --- a/ui-ngx/src/app/shared/components/breadcrumb.component.html +++ b/ui-ngx/src/app/shared/components/breadcrumb.component.html @@ -18,7 +18,7 @@

{{ breadcrumb.ignoreTranslate - ? (breadcrumb.labelFunction ? breadcrumb.labelFunction() : utils.customTranslation(breadcrumb.label, breadcrumb.label)) + ? (breadcrumb.labelFunction ? breadcrumb.labelFunction() : (breadcrumb.label | customTranslate)) : (breadcrumb.label | translate) }}

@@ -41,6 +41,6 @@ {{ breadcrumb.icon }} {{ breadcrumb.ignoreTranslate - ? (breadcrumb.labelFunction ? breadcrumb.labelFunction() : utils.customTranslation(breadcrumb.label, breadcrumb.label)) + ? (breadcrumb.labelFunction ? breadcrumb.labelFunction() : (breadcrumb.label | customTranslate)) : (breadcrumb.label | translate) }} diff --git a/ui-ngx/src/app/shared/components/breadcrumb.component.ts b/ui-ngx/src/app/shared/components/breadcrumb.component.ts index b5255bb0d1..718a1b8900 100644 --- a/ui-ngx/src/app/shared/components/breadcrumb.component.ts +++ b/ui-ngx/src/app/shared/components/breadcrumb.component.ts @@ -14,16 +14,18 @@ /// limitations under the License. /// -import { ChangeDetectionStrategy, ChangeDetectorRef, Component, Input, OnDestroy, OnInit } from '@angular/core'; -import { BehaviorSubject, Subject, Subscription } from 'rxjs'; +import { ChangeDetectionStrategy, ChangeDetectorRef, Component, OnDestroy, OnInit } from '@angular/core'; +import { BehaviorSubject, mergeMap, Subject, Subscription } from 'rxjs'; import { BreadCrumb, BreadCrumbConfig } from './breadcrumb'; import { ActivatedRoute, ActivatedRouteSnapshot, NavigationEnd, Router } from '@angular/router'; -import { distinctUntilChanged, filter, map, tap } from 'rxjs/operators'; +import { distinctUntilChanged, filter, first, map } from 'rxjs/operators'; import { TranslateService } from '@ngx-translate/core'; import { guid } from '@core/utils'; import { BroadcastService } from '@core/services/broadcast.service'; import { ActiveComponentService } from '@core/services/active-component.service'; import { UtilsService } from '@core/services/utils.service'; +import { MenuSection } from '@core/services/menu.models'; +import { MenuService } from '@core/services/menu.service'; @Component({ selector: 'tb-breadcrumb', @@ -44,7 +46,9 @@ export class BreadcrumbComponent implements OnInit, OnDestroy { this.activeComponentValue = activeComponent; if (this.activeComponentValue && this.activeComponentValue.updateBreadcrumbs) { this.updateBreadcrumbsSubscription = this.activeComponentValue.updateBreadcrumbs.subscribe(() => { - this.breadcrumbs$.next(this.buildBreadCrumbs(this.activatedRoute.snapshot)); + this.menuService.availableMenuSections().pipe(first()).subscribe((sections) => { + this.breadcrumbs$.next(this.buildBreadCrumbs(this.activatedRoute.snapshot, sections)); + }); }); } } @@ -54,7 +58,8 @@ export class BreadcrumbComponent implements OnInit, OnDestroy { routerEventsSubscription = this.router.events.pipe( filter((event) => event instanceof NavigationEnd ), distinctUntilChanged(), - map( () => this.buildBreadCrumbs(this.activatedRoute.snapshot) ) + mergeMap(() => this.menuService.availableMenuSections().pipe(first())), + map( (sections) => this.buildBreadCrumbs(this.activatedRoute.snapshot, sections) ) ).subscribe(breadcrumns => this.breadcrumbs$.next(breadcrumns) ); activeComponentSubscription = this.activeComponentService.onActiveComponentChanged().subscribe(comp => this.setActiveComponent(comp)); @@ -69,6 +74,7 @@ export class BreadcrumbComponent implements OnInit, OnDestroy { private activeComponentService: ActiveComponentService, private cd: ChangeDetectorRef, private translate: TranslateService, + private menuService: MenuService, public utils: UtilsService) { } @@ -96,7 +102,8 @@ export class BreadcrumbComponent implements OnInit, OnDestroy { return child; } - buildBreadCrumbs(route: ActivatedRouteSnapshot, breadcrumbs: Array = [], + buildBreadCrumbs(route: ActivatedRouteSnapshot, availableMenuSections: MenuSection[], + breadcrumbs: Array = [], lastChild?: ActivatedRouteSnapshot): Array { if (!lastChild) { lastChild = this.lastChild(route); @@ -105,18 +112,19 @@ export class BreadcrumbComponent implements OnInit, OnDestroy { if (route.routeConfig && route.routeConfig.data) { const breadcrumbConfig = route.routeConfig.data.breadcrumb as BreadCrumbConfig; if (breadcrumbConfig && !breadcrumbConfig.skip) { - let label; - let labelFunction; - let ignoreTranslate; + let labelFunction: () => string; + let ignoreTranslate: boolean; + const section: MenuSection = breadcrumbConfig.menuId ? + availableMenuSections.find(menu => menu.id === breadcrumbConfig.menuId) : null; + const label = section?.name || breadcrumbConfig.label || 'home.home'; if (breadcrumbConfig.labelFunction) { labelFunction = () => this.activeComponentValue ? - breadcrumbConfig.labelFunction(route, this.translate, this.activeComponentValue, lastChild.data) : breadcrumbConfig.label; + breadcrumbConfig.labelFunction(route, this.translate, this.activeComponentValue, lastChild.data) : label; ignoreTranslate = true; } else { - label = breadcrumbConfig.label || 'home.home'; ignoreTranslate = false; } - const icon = breadcrumbConfig.icon || 'home'; + const icon = section?.icon || breadcrumbConfig.icon || 'home'; const link = [ route.pathFromRoot.map(v => v.url.map(segment => segment.toString()).join('/')).join('/') ]; const breadcrumb = { id: guid(), @@ -131,12 +139,12 @@ export class BreadcrumbComponent implements OnInit, OnDestroy { } } if (route.firstChild) { - return this.buildBreadCrumbs(route.firstChild, newBreadcrumbs, lastChild); + return this.buildBreadCrumbs(route.firstChild, availableMenuSections, newBreadcrumbs, lastChild); } return newBreadcrumbs; } - trackByBreadcrumbs(index: number, breadcrumb: BreadCrumb){ + trackByBreadcrumbs(_index: number, breadcrumb: BreadCrumb){ return breadcrumb.id; } } diff --git a/ui-ngx/src/app/shared/components/breadcrumb.ts b/ui-ngx/src/app/shared/components/breadcrumb.ts index 175a0b374d..5adf8b3aa2 100644 --- a/ui-ngx/src/app/shared/components/breadcrumb.ts +++ b/ui-ngx/src/app/shared/components/breadcrumb.ts @@ -17,6 +17,7 @@ import { ActivatedRouteSnapshot, Params } from '@angular/router'; import { TranslateService } from '@ngx-translate/core'; import { HasUUID } from '@shared/models/id/has-uuid'; +import { MenuId } from '@core/services/menu.models'; export interface BreadCrumb extends HasUUID{ label: string; @@ -31,7 +32,8 @@ export type BreadCrumbLabelFunction = (route: ActivatedRouteSnapshot, transla export interface BreadCrumbConfig { labelFunction: BreadCrumbLabelFunction; - label: string; - icon: string; + menuId?: MenuId; + label?: string; + icon?: string; skip: boolean; } From 8febc1ed42951c5b57eb88df7b3442d46c06a98c Mon Sep 17 00:00:00 2001 From: Igor Kulikov Date: Tue, 20 Aug 2024 12:42:44 +0300 Subject: [PATCH 2/9] UI: Fix menuSections subject. --- ui-ngx/src/app/core/services/menu.service.ts | 2 +- ui-ngx/src/app/shared/components/breadcrumb.component.ts | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/ui-ngx/src/app/core/services/menu.service.ts b/ui-ngx/src/app/core/services/menu.service.ts index c51be16c5c..80111ebf41 100644 --- a/ui-ngx/src/app/core/services/menu.service.ts +++ b/ui-ngx/src/app/core/services/menu.service.ts @@ -31,7 +31,7 @@ import { NavigationEnd, Router } from '@angular/router'; export class MenuService { currentMenuSections: Array; - menuSections$: Subject> = new ReplaySubject>(); + menuSections$: Subject> = new ReplaySubject>(1); homeSections$: Subject> = new BehaviorSubject>([]); availableMenuLinks$ = this.menuSections$.pipe( map((items) => this.allMenuLinks(items)) diff --git a/ui-ngx/src/app/shared/components/breadcrumb.component.ts b/ui-ngx/src/app/shared/components/breadcrumb.component.ts index 718a1b8900..cc4ea56bc4 100644 --- a/ui-ngx/src/app/shared/components/breadcrumb.component.ts +++ b/ui-ngx/src/app/shared/components/breadcrumb.component.ts @@ -15,10 +15,10 @@ /// import { ChangeDetectionStrategy, ChangeDetectorRef, Component, OnDestroy, OnInit } from '@angular/core'; -import { BehaviorSubject, mergeMap, Subject, Subscription } from 'rxjs'; +import { BehaviorSubject, Subject, Subscription } from 'rxjs'; import { BreadCrumb, BreadCrumbConfig } from './breadcrumb'; import { ActivatedRoute, ActivatedRouteSnapshot, NavigationEnd, Router } from '@angular/router'; -import { distinctUntilChanged, filter, first, map } from 'rxjs/operators'; +import { distinctUntilChanged, filter, first, map, switchMap } from 'rxjs/operators'; import { TranslateService } from '@ngx-translate/core'; import { guid } from '@core/utils'; import { BroadcastService } from '@core/services/broadcast.service'; @@ -58,7 +58,7 @@ export class BreadcrumbComponent implements OnInit, OnDestroy { routerEventsSubscription = this.router.events.pipe( filter((event) => event instanceof NavigationEnd ), distinctUntilChanged(), - mergeMap(() => this.menuService.availableMenuSections().pipe(first())), + switchMap(() => this.menuService.availableMenuSections().pipe(first())), map( (sections) => this.buildBreadCrumbs(this.activatedRoute.snapshot, sections) ) ).subscribe(breadcrumns => this.breadcrumbs$.next(breadcrumns) ); From bc844d3b7fba4866f359ced454b5774200d404ac Mon Sep 17 00:00:00 2001 From: Igor Kulikov Date: Tue, 20 Aug 2024 13:11:57 +0300 Subject: [PATCH 3/9] UI: Remove redudant disabled field from menu section --- ui-ngx/src/app/core/services/menu.models.ts | 1 - ui-ngx/src/app/modules/home/components/router-tabs.component.ts | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/ui-ngx/src/app/core/services/menu.models.ts b/ui-ngx/src/app/core/services/menu.models.ts index 703397fee1..e775dd75bd 100644 --- a/ui-ngx/src/app/core/services/menu.models.ts +++ b/ui-ngx/src/app/core/services/menu.models.ts @@ -29,7 +29,6 @@ export interface MenuSection { icon: string; pages?: Array; opened?: boolean; - disabled?: boolean; rootOnly?: boolean; } diff --git a/ui-ngx/src/app/modules/home/components/router-tabs.component.ts b/ui-ngx/src/app/modules/home/components/router-tabs.component.ts index 306e1b5af9..681558deb0 100644 --- a/ui-ngx/src/app/modules/home/components/router-tabs.component.ts +++ b/ui-ngx/src/app/modules/home/components/router-tabs.component.ts @@ -107,7 +107,7 @@ export class RouterTabsComponent extends PageComponent implements OnInit { if (found) { const rootPath = sectionPath.substring(0, sectionPath.length - found.path.length); const isRoot = rootPath === ''; - const tabs: Array = found ? found.pages.filter(page => !page.disabled && (!page.rootOnly || isRoot)) : []; + const tabs: Array = found ? found.pages.filter(page => !page.rootOnly || isRoot) : []; return tabs.map((tab) => ({...tab, path: rootPath + tab.path})); } return []; From e97e57a38fb700d07a901ac5a2070168e6cf8a62 Mon Sep 17 00:00:00 2001 From: Igor Kulikov Date: Tue, 20 Aug 2024 13:25:57 +0300 Subject: [PATCH 4/9] UI: Menu optimization --- ui-ngx/src/app/core/services/menu.service.ts | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/ui-ngx/src/app/core/services/menu.service.ts b/ui-ngx/src/app/core/services/menu.service.ts index 80111ebf41..cfa555d627 100644 --- a/ui-ngx/src/app/core/services/menu.service.ts +++ b/ui-ngx/src/app/core/services/menu.service.ts @@ -30,13 +30,13 @@ import { NavigationEnd, Router } from '@angular/router'; }) export class MenuService { - currentMenuSections: Array; - menuSections$: Subject> = new ReplaySubject>(1); - homeSections$: Subject> = new BehaviorSubject>([]); - availableMenuLinks$ = this.menuSections$.pipe( + private currentMenuSections: Array; + private menuSections$: Subject> = new ReplaySubject>(1); + private homeSections$: Subject> = new BehaviorSubject>([]); + private availableMenuLinks$ = this.menuSections$.pipe( map((items) => this.allMenuLinks(items)) ); - availableMenuSections$ = this.menuSections$.pipe( + private availableMenuSections$ = this.menuSections$.pipe( map((items) => this.allMenuSections(items)) ); From c4a129a0bf15d9cea331eb817ecdd59c9edad6a3 Mon Sep 17 00:00:00 2001 From: Igor Kulikov Date: Tue, 20 Aug 2024 13:39:16 +0300 Subject: [PATCH 5/9] UI: Menu improvements. --- .../widget/lib/home-page/quick-link.component.ts | 8 +++++--- ui-ngx/src/app/modules/home/menu/menu-link.component.html | 2 +- .../src/app/modules/home/menu/menu-toggle.component.html | 2 +- .../src/app/shared/components/breadcrumb.component.html | 8 ++------ ui-ngx/src/app/shared/components/breadcrumb.component.ts | 5 ----- ui-ngx/src/app/shared/components/breadcrumb.ts | 1 - 6 files changed, 9 insertions(+), 17 deletions(-) diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/home-page/quick-link.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/home-page/quick-link.component.ts index 7a2e7704f2..efc624f90d 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/home-page/quick-link.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/home-page/quick-link.component.ts @@ -49,6 +49,7 @@ import { PageLink } from '@shared/models/page/page-link'; import { Direction } from '@shared/models/page/sort-order'; import { emptyPageData, PageData } from '@shared/models/page/page-data'; import { deepClone } from '@core/utils'; +import { CustomTranslatePipe } from '@shared/pipe/custom-translate.pipe'; @Component({ selector: 'tb-quick-link', @@ -114,6 +115,7 @@ export class QuickLinkComponent extends PageComponent implements OnInit, Control private fb: UntypedFormBuilder, private menuService: MenuService, public translate: TranslateService, + private customTranslate: CustomTranslatePipe, @SkipSelf() private errorStateMatcher: ErrorStateMatcher) { super(store); } @@ -135,7 +137,7 @@ export class QuickLinkComponent extends PageComponent implements OnInit, Control this.updateView(modelValue); }), map(value => value ? (typeof value === 'string' ? value : - ((value as any).translated ? value.name : this.translate.instant(value.name))) : ''), + ((value as any).translated ? value.name : this.customTranslate.transform(value.name))) : ''), distinctUntilChanged(), switchMap(name => this.fetchLinks(name) ), share() @@ -202,7 +204,7 @@ export class QuickLinkComponent extends PageComponent implements OnInit, Control } displayLinkFn = (link?: MenuSection): string | undefined => - link ? ((link as any).translated ? link.name : this.translate.instant(link.fullName || link.name)) : undefined; + link ? ((link as any).translated ? link.name : this.customTranslate.transform(link.fullName || link.name)) : undefined; fetchLinks(searchText?: string): Observable> { this.searchText = searchText; @@ -228,7 +230,7 @@ export class QuickLinkComponent extends PageComponent implements OnInit, Control map((links) => { const result = deepClone(links); for (const link of result) { - link.name = this.translate.instant(link.fullName || link.name); + link.name = this.customTranslate.transform(link.fullName || link.name); (link as any).translated = true; } return result; diff --git a/ui-ngx/src/app/modules/home/menu/menu-link.component.html b/ui-ngx/src/app/modules/home/menu/menu-link.component.html index b1d7d00bcd..c0c098d5b6 100644 --- a/ui-ngx/src/app/modules/home/menu/menu-link.component.html +++ b/ui-ngx/src/app/modules/home/menu/menu-link.component.html @@ -17,5 +17,5 @@ --> {{section.icon}} - {{section.name | translate}} + {{section.name | customTranslate}} diff --git a/ui-ngx/src/app/modules/home/menu/menu-toggle.component.html b/ui-ngx/src/app/modules/home/menu/menu-toggle.component.html index 9375b04500..13eb0e2095 100644 --- a/ui-ngx/src/app/modules/home/menu/menu-toggle.component.html +++ b/ui-ngx/src/app/modules/home/menu/menu-toggle.component.html @@ -17,7 +17,7 @@ --> {{section.icon}} - {{section.name | translate}} + {{section.name | customTranslate}} diff --git a/ui-ngx/src/app/shared/components/breadcrumb.component.html b/ui-ngx/src/app/shared/components/breadcrumb.component.html index 506555d9e8..3397d74e0d 100644 --- a/ui-ngx/src/app/shared/components/breadcrumb.component.html +++ b/ui-ngx/src/app/shared/components/breadcrumb.component.html @@ -17,9 +17,7 @@ -->

- {{ breadcrumb.ignoreTranslate - ? (breadcrumb.labelFunction ? breadcrumb.labelFunction() : (breadcrumb.label | customTranslate)) - : (breadcrumb.label | translate) }} + {{ breadcrumb.labelFunction ? breadcrumb.labelFunction() : (breadcrumb.label | customTranslate) }}

@@ -40,7 +38,5 @@ {{ breadcrumb.icon }} - {{ breadcrumb.ignoreTranslate - ? (breadcrumb.labelFunction ? breadcrumb.labelFunction() : (breadcrumb.label | customTranslate)) - : (breadcrumb.label | translate) }} + {{ breadcrumb.labelFunction ? breadcrumb.labelFunction() : (breadcrumb.label | customTranslate) }} diff --git a/ui-ngx/src/app/shared/components/breadcrumb.component.ts b/ui-ngx/src/app/shared/components/breadcrumb.component.ts index cc4ea56bc4..9965560b55 100644 --- a/ui-ngx/src/app/shared/components/breadcrumb.component.ts +++ b/ui-ngx/src/app/shared/components/breadcrumb.component.ts @@ -113,16 +113,12 @@ export class BreadcrumbComponent implements OnInit, OnDestroy { const breadcrumbConfig = route.routeConfig.data.breadcrumb as BreadCrumbConfig; if (breadcrumbConfig && !breadcrumbConfig.skip) { let labelFunction: () => string; - let ignoreTranslate: boolean; const section: MenuSection = breadcrumbConfig.menuId ? availableMenuSections.find(menu => menu.id === breadcrumbConfig.menuId) : null; const label = section?.name || breadcrumbConfig.label || 'home.home'; if (breadcrumbConfig.labelFunction) { labelFunction = () => this.activeComponentValue ? breadcrumbConfig.labelFunction(route, this.translate, this.activeComponentValue, lastChild.data) : label; - ignoreTranslate = true; - } else { - ignoreTranslate = false; } const icon = section?.icon || breadcrumbConfig.icon || 'home'; const link = [ route.pathFromRoot.map(v => v.url.map(segment => segment.toString()).join('/')).join('/') ]; @@ -130,7 +126,6 @@ export class BreadcrumbComponent implements OnInit, OnDestroy { id: guid(), label, labelFunction, - ignoreTranslate, icon, link, queryParams: null diff --git a/ui-ngx/src/app/shared/components/breadcrumb.ts b/ui-ngx/src/app/shared/components/breadcrumb.ts index 5adf8b3aa2..9c85cc5a19 100644 --- a/ui-ngx/src/app/shared/components/breadcrumb.ts +++ b/ui-ngx/src/app/shared/components/breadcrumb.ts @@ -22,7 +22,6 @@ import { MenuId } from '@core/services/menu.models'; export interface BreadCrumb extends HasUUID{ label: string; labelFunction?: () => string; - ignoreTranslate: boolean; icon: string; link: any[]; queryParams: Params; From 4a5dabdad8913c845bd8ce616ae52a61965b390e Mon Sep 17 00:00:00 2001 From: Igor Kulikov Date: Tue, 20 Aug 2024 14:00:15 +0300 Subject: [PATCH 6/9] UI: Improve menu translation --- ui-ngx/src/app/core/services/menu.models.ts | 1 + .../widget/lib/home-page/quick-link.component.ts | 9 ++++++--- .../src/app/modules/home/menu/menu-link.component.html | 2 +- .../src/app/modules/home/menu/menu-toggle.component.html | 2 +- .../src/app/shared/components/breadcrumb.component.html | 4 ++-- ui-ngx/src/app/shared/components/breadcrumb.component.ts | 2 ++ ui-ngx/src/app/shared/components/breadcrumb.ts | 1 + 7 files changed, 14 insertions(+), 7 deletions(-) diff --git a/ui-ngx/src/app/core/services/menu.models.ts b/ui-ngx/src/app/core/services/menu.models.ts index e775dd75bd..7afe08f929 100644 --- a/ui-ngx/src/app/core/services/menu.models.ts +++ b/ui-ngx/src/app/core/services/menu.models.ts @@ -30,6 +30,7 @@ export interface MenuSection { pages?: Array; opened?: boolean; rootOnly?: boolean; + customTranslate?: boolean; } export interface MenuReference { diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/home-page/quick-link.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/home-page/quick-link.component.ts index efc624f90d..2662605e2a 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/home-page/quick-link.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/home-page/quick-link.component.ts @@ -137,7 +137,8 @@ export class QuickLinkComponent extends PageComponent implements OnInit, Control this.updateView(modelValue); }), map(value => value ? (typeof value === 'string' ? value : - ((value as any).translated ? value.name : this.customTranslate.transform(value.name))) : ''), + ((value as any).translated ? value.name + : value.customTranslate ? this.customTranslate.transform(value.name) : this.translate.instant(value.name))) : ''), distinctUntilChanged(), switchMap(name => this.fetchLinks(name) ), share() @@ -204,7 +205,8 @@ export class QuickLinkComponent extends PageComponent implements OnInit, Control } displayLinkFn = (link?: MenuSection): string | undefined => - link ? ((link as any).translated ? link.name : this.customTranslate.transform(link.fullName || link.name)) : undefined; + link ? ((link as any).translated ? link.name : link.customTranslate ? this.customTranslate.transform(link.fullName || link.name) + : this.translate.instant(link.fullName || link.name)) : undefined; fetchLinks(searchText?: string): Observable> { this.searchText = searchText; @@ -230,7 +232,8 @@ export class QuickLinkComponent extends PageComponent implements OnInit, Control map((links) => { const result = deepClone(links); for (const link of result) { - link.name = this.customTranslate.transform(link.fullName || link.name); + link.name = link.customTranslate ? this.customTranslate.transform(link.fullName || link.name) + : this.translate.instant(link.fullName || link.name); (link as any).translated = true; } return result; diff --git a/ui-ngx/src/app/modules/home/menu/menu-link.component.html b/ui-ngx/src/app/modules/home/menu/menu-link.component.html index c0c098d5b6..fea7165214 100644 --- a/ui-ngx/src/app/modules/home/menu/menu-link.component.html +++ b/ui-ngx/src/app/modules/home/menu/menu-link.component.html @@ -17,5 +17,5 @@ --> {{section.icon}} - {{section.name | customTranslate}} + {{section.customTranslate ? (section.name | customTranslate) : (section.name | translate)}} diff --git a/ui-ngx/src/app/modules/home/menu/menu-toggle.component.html b/ui-ngx/src/app/modules/home/menu/menu-toggle.component.html index 13eb0e2095..3d488e9229 100644 --- a/ui-ngx/src/app/modules/home/menu/menu-toggle.component.html +++ b/ui-ngx/src/app/modules/home/menu/menu-toggle.component.html @@ -17,7 +17,7 @@ --> {{section.icon}} - {{section.name | customTranslate}} + {{section.customTranslate ? (section.name | customTranslate) : (section.name | translate)}} diff --git a/ui-ngx/src/app/shared/components/breadcrumb.component.html b/ui-ngx/src/app/shared/components/breadcrumb.component.html index 3397d74e0d..a80d3b375d 100644 --- a/ui-ngx/src/app/shared/components/breadcrumb.component.html +++ b/ui-ngx/src/app/shared/components/breadcrumb.component.html @@ -17,7 +17,7 @@ -->

- {{ breadcrumb.labelFunction ? breadcrumb.labelFunction() : (breadcrumb.label | customTranslate) }} + {{ breadcrumb.labelFunction ? breadcrumb.labelFunction() : (breadcrumb.customTranslate ? (breadcrumb.label | customTranslate) : (breadcrumb.label | translate)) }}

@@ -38,5 +38,5 @@ {{ breadcrumb.icon }} - {{ breadcrumb.labelFunction ? breadcrumb.labelFunction() : (breadcrumb.label | customTranslate) }} + {{ breadcrumb.labelFunction ? breadcrumb.labelFunction() : (breadcrumb.customTranslate ? (breadcrumb.label | customTranslate) : (breadcrumb.label | translate)) }} diff --git a/ui-ngx/src/app/shared/components/breadcrumb.component.ts b/ui-ngx/src/app/shared/components/breadcrumb.component.ts index 9965560b55..23c9abe337 100644 --- a/ui-ngx/src/app/shared/components/breadcrumb.component.ts +++ b/ui-ngx/src/app/shared/components/breadcrumb.component.ts @@ -116,6 +116,7 @@ export class BreadcrumbComponent implements OnInit, OnDestroy { const section: MenuSection = breadcrumbConfig.menuId ? availableMenuSections.find(menu => menu.id === breadcrumbConfig.menuId) : null; const label = section?.name || breadcrumbConfig.label || 'home.home'; + const customTranslate = section?.customTranslate || false; if (breadcrumbConfig.labelFunction) { labelFunction = () => this.activeComponentValue ? breadcrumbConfig.labelFunction(route, this.translate, this.activeComponentValue, lastChild.data) : label; @@ -125,6 +126,7 @@ export class BreadcrumbComponent implements OnInit, OnDestroy { const breadcrumb = { id: guid(), label, + customTranslate, labelFunction, icon, link, diff --git a/ui-ngx/src/app/shared/components/breadcrumb.ts b/ui-ngx/src/app/shared/components/breadcrumb.ts index 9c85cc5a19..1f5ad82820 100644 --- a/ui-ngx/src/app/shared/components/breadcrumb.ts +++ b/ui-ngx/src/app/shared/components/breadcrumb.ts @@ -21,6 +21,7 @@ import { MenuId } from '@core/services/menu.models'; export interface BreadCrumb extends HasUUID{ label: string; + customTranslate: boolean; labelFunction?: () => string; icon: string; link: any[]; From 72134abbf9a5bd03dd4efcd6e7c53b76f2fc0972 Mon Sep 17 00:00:00 2001 From: Igor Kulikov Date: Tue, 20 Aug 2024 14:46:28 +0300 Subject: [PATCH 7/9] UI: Home links refactoring. --- ui-ngx/src/app/core/services/menu.models.ts | 118 ++++++- ui-ngx/src/app/core/services/menu.service.ts | 324 +----------------- .../navigation-cards-widget.component.html | 2 +- .../lib/navigation-cards-widget.component.ts | 10 +- .../home-links/home-links.component.html | 2 +- 5 files changed, 126 insertions(+), 330 deletions(-) diff --git a/ui-ngx/src/app/core/services/menu.models.ts b/ui-ngx/src/app/core/services/menu.models.ts index 7afe08f929..e27d015fbe 100644 --- a/ui-ngx/src/app/core/services/menu.models.ts +++ b/ui-ngx/src/app/core/services/menu.models.ts @@ -38,15 +38,14 @@ export interface MenuReference { pages?: Array; } -export interface HomeSection { +export interface HomeSectionReference { name: string; - places: Array; + places: Array; } -export interface HomeSectionPlace { +export interface HomeSection { name: string; - icon: string; - path: string; + places: Array; } export enum MenuId { @@ -768,11 +767,108 @@ const defaultUserMenuMap = new Map([ ] ]); +const defaultHomeSectionMap = new Map([ + [ + Authority.SYS_ADMIN, + [ + { + name: 'tenant.management', + places: [MenuId.tenants, MenuId.tenant_profiles] + }, + { + name: 'widget.management', + places: [MenuId.widget_library] + }, + { + name: 'admin.system-settings', + places: [MenuId.general, MenuId.mail_server, + MenuId.notification_settings, MenuId.security_settings, MenuId.oauth2, MenuId.two_fa, MenuId.resources_library, MenuId.queues] + } + ] + ], + [ + Authority.TENANT_ADMIN, + [ + { + name: 'rulechain.management', + places: [MenuId.rule_chains] + }, + { + name: 'customer.management', + places: [MenuId.customers] + }, + { + name: 'asset.management', + places: [MenuId.assets, MenuId.asset_profiles] + }, + { + name: 'device.management', + places: [MenuId.devices, MenuId.device_profiles, MenuId.otaUpdates] + }, + { + name: 'entity-view.management', + places: [MenuId.entity_views] + }, + { + name: 'edge.management', + places: [MenuId.edges, MenuId.rulechain_templates] + }, + { + name: 'dashboard.management', + places: [MenuId.widget_library, MenuId.dashboards] + }, + { + name: 'version-control.management', + places: [MenuId.version_control] + }, + { + name: 'audit-log.audit', + places: [MenuId.audit_log, MenuId.api_usage] + }, + { + name: 'admin.system-settings', + places: [MenuId.home_settings, MenuId.resources_library, MenuId.repository_settings, MenuId.auto_commit_settings] + } + ] + ], + [ + Authority.CUSTOMER_USER, + [ + { + name: 'asset.view-assets', + places: [MenuId.assets] + }, + { + name: 'device.view-devices', + places: [MenuId.devices] + }, + { + name: 'entity-view.management', + places: [MenuId.entity_views] + }, + { + name: 'edge.management', + places: [MenuId.edges] + }, + { + name: 'dashboard.view-dashboards', + places: [MenuId.dashboards] + } + ] + ] +]); + export const buildUserMenu = (authState: AuthState): Array => { const references = defaultUserMenuMap.get(authState.authUser.authority); return (references || []).map(ref => referenceToMenuSection(authState, ref)).filter(section => !!section); }; +export const buildUserHome = (authState: AuthState, availableMenuSections: MenuSection[]): Array => { + const references = defaultHomeSectionMap.get(authState.authUser.authority); + return (references || []).map(ref => + homeReferenceToHomeSection(availableMenuSections, ref)).filter(section => !!section); +}; + const referenceToMenuSection = (authState: AuthState, reference: MenuReference): MenuSection | undefined => { if (filterMenuReference(authState, reference)) { const section = menuSectionMap.get(reference.id); @@ -807,3 +903,15 @@ const filterMenuReference = (authState: AuthState, reference: MenuReference): bo return true; } }; + +const homeReferenceToHomeSection = (availableMenuSections: MenuSection[], reference: HomeSectionReference): HomeSection | undefined => { + const places = reference.places.map(id => availableMenuSections.find(m => m.id === id)).filter(p => !!p); + if (places.length) { + return { + name: reference.name, + places + }; + } else { + return undefined; + } +}; diff --git a/ui-ngx/src/app/core/services/menu.service.ts b/ui-ngx/src/app/core/services/menu.service.ts index cfa555d627..90976e282c 100644 --- a/ui-ngx/src/app/core/services/menu.service.ts +++ b/ui-ngx/src/app/core/services/menu.service.ts @@ -19,9 +19,8 @@ import { select, Store } from '@ngrx/store'; import { AppState } from '../core.state'; import { getCurrentOpenedMenuSections, selectAuth, selectIsAuthenticated } from '../auth/auth.selectors'; import { filter, map, take } from 'rxjs/operators'; -import { buildUserMenu, HomeSection, MenuId, MenuSection } from '@core/services/menu.models'; -import { BehaviorSubject, ReplaySubject, Observable, Subject } from 'rxjs'; -import { Authority } from '@shared/models/authority.enum'; +import { buildUserHome, buildUserMenu, HomeSection, MenuId, MenuSection } from '@core/services/menu.models'; +import { Observable, ReplaySubject, Subject } from 'rxjs'; import { AuthState } from '@core/auth/auth.models'; import { NavigationEnd, Router } from '@angular/router'; @@ -32,13 +31,11 @@ export class MenuService { private currentMenuSections: Array; private menuSections$: Subject> = new ReplaySubject>(1); - private homeSections$: Subject> = new BehaviorSubject>([]); + private homeSections$: Subject> = new ReplaySubject>(1); + private availableMenuSections$: Subject> = new ReplaySubject>(1); private availableMenuLinks$ = this.menuSections$.pipe( map((items) => this.allMenuLinks(items)) ); - private availableMenuSections$ = this.menuSections$.pipe( - map((items) => this.allMenuSections(items)) - ); constructor(private store: Store, private router: Router) { @@ -60,21 +57,12 @@ export class MenuService { this.store.pipe(select(selectAuth), take(1)).subscribe( (authState: AuthState) => { if (authState.authUser) { - let homeSections: Array; - switch (authState.authUser.authority) { - case Authority.SYS_ADMIN: - homeSections = this.buildSysAdminHome(); - break; - case Authority.TENANT_ADMIN: - homeSections = this.buildTenantAdminHome(authState); - break; - case Authority.CUSTOMER_USER: - homeSections = this.buildCustomerUserHome(authState); - break; - } this.currentMenuSections = buildUserMenu(authState); this.updateOpenedMenuSections(); this.menuSections$.next(this.currentMenuSections); + const availableMenuSections = this.allMenuSections(this.currentMenuSections); + this.availableMenuSections$.next(availableMenuSections); + const homeSections = buildUserHome(authState, availableMenuSections); this.homeSections$.next(homeSections); } } @@ -90,304 +78,6 @@ export class MenuService { ); } - private buildSysAdminHome(): Array { - const homeSections: Array = []; - homeSections.push( - { - name: 'tenant.management', - places: [ - { - name: 'tenant.tenants', - icon: 'supervisor_account', - path: '/tenants' - }, - { - name: 'tenant-profile.tenant-profiles', - icon: 'mdi:alpha-t-box', - path: '/tenantProfiles' - }, - ] - }, - { - name: 'widget.management', - places: [ - { - name: 'widget.widget-library', - icon: 'now_widgets', - path: '/resources/widgets-library', - } - ] - }, - { - name: 'admin.system-settings', - places: [ - { - name: 'admin.general', - icon: 'settings_applications', - path: '/settings/general' - }, - { - name: 'admin.outgoing-mail', - icon: 'mail', - path: '/settings/outgoing-mail' - }, - { - name: 'admin.sms-provider', - icon: 'sms', - path: '/settings/sms-provider' - }, - { - name: 'admin.security-settings', - icon: 'security', - path: '/settings/security-settings' - }, - { - name: 'admin.oauth2.oauth2', - icon: 'security', - path: '/settings/oauth2' - }, - { - name: 'admin.2fa.2fa', - icon: 'mdi:two-factor-authentication', - path: '/settings/2fa' - }, - { - name: 'resource.resources-library', - icon: 'folder', - path: '/settings/resources-library' - }, - { - name: 'admin.queues', - icon: 'swap_calls', - path: '/settings/queues' - }, - ] - } - ); - return homeSections; - } - - private buildTenantAdminHome(authState: AuthState): Array { - const homeSections: Array = []; - homeSections.push( - { - name: 'rulechain.management', - places: [ - { - name: 'rulechain.rulechains', - icon: 'settings_ethernet', - path: '/ruleChains' - } - ] - }, - { - name: 'customer.management', - places: [ - { - name: 'customer.customers', - icon: 'supervisor_account', - path: '/customers' - } - ] - }, - { - name: 'asset.management', - places: [ - { - name: 'asset.assets', - icon: 'domain', - path: '/assets' - }, - { - name: 'asset-profile.asset-profiles', - icon: 'mdi:alpha-a-box', - path: '/profiles/assetProfiles' - } - ] - }, - { - name: 'device.management', - places: [ - { - name: 'device.devices', - icon: 'devices_other', - path: '/devices' - }, - { - name: 'device-profile.device-profiles', - icon: 'mdi:alpha-d-box', - path: '/profiles/deviceProfiles' - }, - { - name: 'ota-update.ota-updates', - icon: 'memory', - path: '/otaUpdates' - } - ] - }, - { - name: 'entity-view.management', - places: [ - { - name: 'entity-view.entity-views', - icon: 'view_quilt', - path: '/entityViews' - } - ] - } - ); - if (authState.edgesSupportEnabled) { - homeSections.push( - { - name: 'edge.management', - places: [ - { - name: 'edge.edge-instances', - icon: 'router', - path: '/edgeInstances' - }, - { - name: 'edge.rulechain-templates', - icon: 'settings_ethernet', - path: '/edgeManagement/ruleChains' - } - ] - } - ); - } - homeSections.push( - { - name: 'dashboard.management', - places: [ - { - name: 'widget.widget-library', - icon: 'now_widgets', - path: '/widgets-bundles' - }, - { - name: 'dashboard.dashboards', - icon: 'dashboard', - path: '/dashboards' - } - ] - }, - { - name: 'version-control.management', - places: [ - { - name: 'version-control.version-control', - icon: 'history', - path: '/vc' - } - ] - }, - { - name: 'audit-log.audit', - places: [ - { - name: 'audit-log.audit-logs', - icon: 'track_changes', - path: '/auditLogs' - }, - { - name: 'api-usage.api-usage', - icon: 'insert_chart', - path: '/usage' - } - ] - }, - { - name: 'admin.system-settings', - places: [ - { - name: 'admin.home-settings', - icon: 'settings_applications', - path: '/settings/home' - }, - { - name: 'resource.resources-library', - icon: 'folder', - path: '/settings/resources-library' - }, - { - name: 'admin.repository-settings', - icon: 'manage_history', - path: '/settings/repository', - }, - { - name: 'admin.auto-commit-settings', - icon: 'settings_backup_restore', - path: '/settings/auto-commit' - } - ] - } - ); - return homeSections; - } - - private buildCustomerUserHome(authState: AuthState): Array { - const homeSections: Array = []; - homeSections.push( - { - name: 'asset.view-assets', - places: [ - { - name: 'asset.assets', - icon: 'domain', - path: '/assets' - } - ] - }, - { - name: 'device.view-devices', - places: [ - { - name: 'device.devices', - icon: 'devices_other', - path: '/devices' - } - ] - }, - { - name: 'entity-view.management', - places: [ - { - name: 'entity-view.entity-views', - icon: 'view_quilt', - path: '/entityViews' - } - ] - } - ); - if (authState.edgesSupportEnabled) { - homeSections.push( - { - name: 'edge.management', - places: [ - { - name: 'edge.edge-instances', - icon: 'settings_input_antenna', - path: '/edgeInstances' - } - ] - } - ); - } - homeSections.push( - { - name: 'dashboard.view-dashboards', - places: [ - { - name: 'dashboard.dashboards', - icon: 'dashboard', - path: '/dashboards' - } - ] - } - ); - return homeSections; - } - private allMenuLinks(sections: Array): Array { const result: Array = []; for (const section of sections) { diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/navigation-cards-widget.component.html b/ui-ngx/src/app/modules/home/components/widget/lib/navigation-cards-widget.component.html index 72b154d89a..0d59ed8564 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/navigation-cards-widget.component.html +++ b/ui-ngx/src/app/modules/home/components/widget/lib/navigation-cards-widget.component.html @@ -26,7 +26,7 @@ {{place.icon}} - {{place.name}} + {{place.customTranslate ? (place.name | customTranslate) : (place.name | translate)}} diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/navigation-cards-widget.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/navigation-cards-widget.component.ts index 8ff8fa74e9..539b77f34a 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/navigation-cards-widget.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/navigation-cards-widget.component.ts @@ -20,7 +20,7 @@ import { WidgetContext } from '@home/models/widget-component.models'; import { Store } from '@ngrx/store'; import { AppState } from '@core/core.state'; import { MenuService } from '@core/services/menu.service'; -import { HomeSection, HomeSectionPlace } from '@core/services/menu.models'; +import { HomeSection, MenuSection } from '@core/services/menu.models'; import { Router } from '@angular/router'; import { map } from 'rxjs/operators'; @@ -38,9 +38,7 @@ export class NavigationCardsWidgetComponent extends PageComponent implements OnI homeSections$ = this.menuService.homeSections(); showHomeSections$ = this.homeSections$.pipe( - map((sections) => { - return sections.filter((section) => this.sectionPlaces(section).length > 0); - }) + map((sections) => sections.filter((section) => this.sectionPlaces(section).length > 0)) ); cols = null; @@ -85,11 +83,11 @@ export class NavigationCardsWidgetComponent extends PageComponent implements OnI }); } - sectionPlaces(section: HomeSection): HomeSectionPlace[] { + sectionPlaces(section: HomeSection): MenuSection[] { return section && section.places ? section.places.filter((place) => this.filterPlace(place)) : []; } - private filterPlace(place: HomeSectionPlace): boolean { + private filterPlace(place: MenuSection): boolean { if (this.settings.filterType === 'include') { return this.settings.filter.includes(place.path); } else if (this.settings.filterType === 'exclude') { diff --git a/ui-ngx/src/app/modules/home/pages/home-links/home-links.component.html b/ui-ngx/src/app/modules/home/pages/home-links/home-links.component.html index 132d0e2c65..81142c501f 100644 --- a/ui-ngx/src/app/modules/home/pages/home-links/home-links.component.html +++ b/ui-ngx/src/app/modules/home/pages/home-links/home-links.component.html @@ -28,7 +28,7 @@ {{place.icon}} - {{place.name}} + {{place.customTranslate ? (place.name | customTranslate) : (place.name | translate)}} From bf0d4685105816f38cb4beba714928f0c6907958 Mon Sep 17 00:00:00 2001 From: Igor Kulikov Date: Tue, 20 Aug 2024 15:31:43 +0300 Subject: [PATCH 8/9] UI: Breadcrumb improvements. --- ui-ngx/src/app/core/services/menu.models.ts | 16 ++++++++++++++-- .../shared/components/breadcrumb.component.ts | 11 ++++++++--- 2 files changed, 22 insertions(+), 5 deletions(-) diff --git a/ui-ngx/src/app/core/services/menu.models.ts b/ui-ngx/src/app/core/services/menu.models.ts index e27d015fbe..0d74e37c5f 100644 --- a/ui-ngx/src/app/core/services/menu.models.ts +++ b/ui-ngx/src/app/core/services/menu.models.ts @@ -92,6 +92,7 @@ export enum MenuId { rule_chains = 'rule_chains', edge_management = 'edge_management', edges = 'edges', + edge_instances = 'edge_instances', rulechain_templates = 'rulechain_templates', features = 'features', otaUpdates = 'otaUpdates', @@ -101,7 +102,7 @@ export enum MenuId { declare type MenuFilter = (authState: AuthState) => boolean; -const menuSectionMap = new Map([ +export const menuSectionMap = new Map([ [ MenuId.home, { @@ -545,6 +546,17 @@ const menuSectionMap = new Map([ icon: 'router' } ], + [ + MenuId.edge_instances, + { + id: MenuId.edge_instances, + name: 'edge.edge-instances', + fullName: 'edge.edge-instances', + type: 'link', + path: '/edgeManagement/instances', + icon: 'router' + } + ], [ MenuId.rulechain_templates, { @@ -756,7 +768,7 @@ const defaultUserMenuMap = new Map([ {id: MenuId.entity_views} ] }, - {id: MenuId.edges}, + {id: MenuId.edge_instances}, { id: MenuId.notifications_center, pages: [ diff --git a/ui-ngx/src/app/shared/components/breadcrumb.component.ts b/ui-ngx/src/app/shared/components/breadcrumb.component.ts index 23c9abe337..c0899d7ef7 100644 --- a/ui-ngx/src/app/shared/components/breadcrumb.component.ts +++ b/ui-ngx/src/app/shared/components/breadcrumb.component.ts @@ -24,7 +24,7 @@ import { guid } from '@core/utils'; import { BroadcastService } from '@core/services/broadcast.service'; import { ActiveComponentService } from '@core/services/active-component.service'; import { UtilsService } from '@core/services/utils.service'; -import { MenuSection } from '@core/services/menu.models'; +import { MenuSection, menuSectionMap } from '@core/services/menu.models'; import { MenuService } from '@core/services/menu.service'; @Component({ @@ -113,8 +113,13 @@ export class BreadcrumbComponent implements OnInit, OnDestroy { const breadcrumbConfig = route.routeConfig.data.breadcrumb as BreadCrumbConfig; if (breadcrumbConfig && !breadcrumbConfig.skip) { let labelFunction: () => string; - const section: MenuSection = breadcrumbConfig.menuId ? - availableMenuSections.find(menu => menu.id === breadcrumbConfig.menuId) : null; + let section: MenuSection = null; + if (breadcrumbConfig.menuId) { + section = availableMenuSections.find(menu => menu.id === breadcrumbConfig.menuId); + if (!section) { + section = menuSectionMap.get(breadcrumbConfig.menuId); + } + } const label = section?.name || breadcrumbConfig.label || 'home.home'; const customTranslate = section?.customTranslate || false; if (breadcrumbConfig.labelFunction) { From 7e9950c9f5c2d229f8a8dba05123038ccea0d7d8 Mon Sep 17 00:00:00 2001 From: Igor Kulikov Date: Tue, 20 Aug 2024 15:38:50 +0300 Subject: [PATCH 9/9] Minor improvements --- ui-ngx/src/app/core/services/menu.models.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ui-ngx/src/app/core/services/menu.models.ts b/ui-ngx/src/app/core/services/menu.models.ts index 0d74e37c5f..d11b28a97f 100644 --- a/ui-ngx/src/app/core/services/menu.models.ts +++ b/ui-ngx/src/app/core/services/menu.models.ts @@ -860,7 +860,7 @@ const defaultHomeSectionMap = new Map([ }, { name: 'edge.management', - places: [MenuId.edges] + places: [MenuId.edge_instances] }, { name: 'dashboard.view-dashboards',