/// /// Copyright © 2016-2025 The Thingsboard Authors /// /// Licensed under the Apache License, Version 2.0 (the "License"); /// you may not use this file except in compliance with the License. /// You may obtain a copy of the License at /// /// http://www.apache.org/licenses/LICENSE-2.0 /// /// Unless required by applicable law or agreed to in writing, software /// distributed under the License is distributed on an "AS IS" BASIS, /// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. /// See the License for the specific language governing permissions and /// limitations under the License. /// 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 { id: MenuId | string; name: string; fullName?: string; type: MenuSectionType; path: string; icon: string; pages?: Array; opened?: boolean; rootOnly?: boolean; customTranslate?: boolean; } export interface MenuReference { id: MenuId; pages?: Array; } export interface HomeSectionReference { name: string; places: Array; } export interface HomeSection { name: string; places: Array; } 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', javascript_library = 'javascript_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', mobile_center = 'mobile_center', mobile_apps = 'mobile_apps', mobile_bundles = 'mobile_bundles', mobile_qr_code_widget = 'mobile_qr_code_widget', 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', security_settings = 'security_settings', security_settings_general = 'security_settings_general', two_fa = 'two_fa', oauth2 = 'oauth2', domains = 'domains', clients = 'clients', audit_log = 'audit_log', alarms = 'alarms', dashboards = 'dashboards', entities = 'entities', devices = 'devices', assets = 'assets', entity_views = 'entity_views', gateways = 'gateways', profiles = 'profiles', device_profiles = 'device_profiles', asset_profiles = 'asset_profiles', customers = 'customers', rule_chains = 'rule_chains', edge_management = 'edge_management', edges = 'edges', edge_instances = 'edge_instances', rulechain_templates = 'rulechain_templates', features = 'features', otaUpdates = 'otaUpdates', version_control = 'version_control', api_usage = 'api_usage', trendz_settings = 'trendz_settings' } declare type MenuFilter = (authState: AuthState) => boolean; export 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.javascript_library, { id: MenuId.javascript_library, name: 'javascript.javascript-library', type: 'link', path: '/resources/javascript-library', icon: 'mdi:language-javascript' } ], [ 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.mobile_center, { id: MenuId.mobile_center, name: 'mobile.mobile-center', type: 'link', path: '/mobile-center', icon: 'smartphone' } ], [ MenuId.mobile_apps, { id: MenuId.mobile_apps, name: 'mobile.applications', type: 'link', path: '/mobile-center/applications', icon: 'list' } ], [ MenuId.mobile_bundles, { id: MenuId.mobile_bundles, name: 'mobile.bundles', type: 'link', path: '/mobile-center/bundles', icon: 'mdi:package' } ], [ MenuId.mobile_qr_code_widget, { id: MenuId.mobile_qr_code_widget, name: 'mobile.qr-code-widget', fullName: 'mobile.qr-code-widget', type: 'link', path: '/mobile-center/qr-code-widget', icon: 'qr_code' } ], [ 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.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.domains, { id: MenuId.domains, name: 'admin.oauth2.domains', type: 'link', path: '/security-settings/oauth2/domains', icon: 'domain' } ], [ MenuId.clients, { id: MenuId.clients, name: 'admin.oauth2.clients', type: 'link', path: '/security-settings/oauth2/clients', icon: 'public' } ], [ 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.gateways, { id: MenuId.gateways, name: 'gateway.gateways', type: 'link', path: '/entities/gateways', icon: 'lan' } ], [ 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.edge_instances, { id: MenuId.edge_instances, name: 'edge.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' } ], [ MenuId.trendz_settings, { id: MenuId.trendz_settings, name: 'admin.trendz', fullName: 'admin.trendz-settings', type: 'link', path: '/settings/trendz', icon: 'trendz-settings' } ] ]); 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.javascript_library}, {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.mobile_center, pages: [ {id: MenuId.mobile_bundles}, {id: MenuId.mobile_apps}, {id: MenuId.mobile_qr_code_widget} ] }, { id: MenuId.settings, pages: [ {id: MenuId.general}, {id: MenuId.mail_server}, {id: MenuId.notification_settings}, {id: MenuId.queues} ] }, { id: MenuId.security_settings, pages: [ {id: MenuId.security_settings_general}, {id: MenuId.two_fa}, { id: MenuId.oauth2, pages: [ {id: MenuId.domains}, {id: MenuId.clients} ] } ] } ] ], [ 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.gateways} ] }, { 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.javascript_library}, {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.mobile_center, pages: [ {id: MenuId.mobile_bundles}, {id: MenuId.mobile_apps} ] }, {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.trendz_settings} ] }, { id: MenuId.security_settings, pages: [ {id: MenuId.audit_log}, { id: MenuId.oauth2, pages: [ {id: MenuId.clients} ] } ] } ] ], [ 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.edge_instances}, { id: MenuId.notifications_center, pages: [ {id: MenuId.notification_inbox} ] } ] ] ]); 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.domains, MenuId.clients, 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, MenuId.trendz_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.edge_instances] }, { 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); 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; } }; 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; } };