From 916faae7743fea7069a50daa55635cafcd9f5089 Mon Sep 17 00:00:00 2001 From: Igor Kulikov Date: Mon, 13 Feb 2023 19:09:05 +0200 Subject: [PATCH] UI: Menu improvements --- ui-ngx/src/app/core/http/alarm.service.ts | 6 + ui-ngx/src/app/core/services/menu.service.ts | 333 +++++++++---- .../components/alarm/alarm-table-config.ts | 16 +- .../alarm/alarm-table.component.html | 2 +- .../alarm/alarm-table.component.scss | 2 +- .../components/alarm/alarm-table.component.ts | 20 +- .../profile/alarm/alarm-rule.component.ts | 6 +- .../alarm/create-alarm-rules.component.ts | 12 +- .../alarm/device-profile-alarm.component.ts | 4 +- .../home/pages/admin/admin-routing.module.ts | 111 +---- .../home/pages/alarm/alarm-routing.module.ts | 140 ++++++ .../alarm/alarm-rule-tabs.component.html | 21 + .../pages/alarm/alarm-rule-tabs.component.ts | 38 ++ .../pages/alarm/alarm-rule.component.html | 47 ++ .../home/pages/alarm/alarm-rule.component.ts | 63 +++ .../alarm-rules-table-config.resolver.ts | 100 ++++ .../modules/home/pages/alarm/alarm.module.ts | 39 ++ .../home/pages/asset/asset-routing.module.ts | 14 +- .../audit-log/audit-log-routing.module.ts | 12 +- .../pages/device/device-routing.module.ts | 16 +- .../home/pages/edge/edge-routing.module.ts | 438 ++++++++++-------- .../pages/edge/edges-table-config.resolver.ts | 2 +- .../pages/entities/entities-routing.module.ts | 54 +++ .../home/pages/entities/entities.module.ts | 30 ++ .../entity-view/entity-view-routing.module.ts | 14 +- .../pages/features/features-routing.module.ts | 54 +++ .../home/pages/features/features.module.ts | 30 ++ .../modules/home/pages/home-pages.module.ts | 8 + .../notification-routing.module.ts | 135 ++++++ .../pages/notification/notification.module.ts | 30 ++ .../ota-update/ota-update-routing.module.ts | 14 +- .../rulechain/rulechain-routing.module.ts | 20 +- .../rulechains-table-config.resolver.ts | 2 +- .../home/pages/vc/vc-routing.module.ts | 9 +- .../widget/widget-library-routing.module.ts | 96 +++- .../app/shared/models/alarm-rule.models.ts | 24 + ui-ngx/src/app/shared/models/alarm.models.ts | 7 +- ui-ngx/src/app/shared/models/device.models.ts | 4 +- .../app/shared/models/entity-type.models.ts | 36 +- .../src/app/shared/models/id/alarm-rule-id.ts | 26 ++ ui-ngx/src/app/shared/models/id/public-api.ts | 1 + ui-ngx/src/app/shared/models/public-api.ts | 1 + .../assets/locale/locale.constant-en_US.json | 40 +- 43 files changed, 1641 insertions(+), 436 deletions(-) create mode 100644 ui-ngx/src/app/modules/home/pages/alarm/alarm-routing.module.ts create mode 100644 ui-ngx/src/app/modules/home/pages/alarm/alarm-rule-tabs.component.html create mode 100644 ui-ngx/src/app/modules/home/pages/alarm/alarm-rule-tabs.component.ts create mode 100644 ui-ngx/src/app/modules/home/pages/alarm/alarm-rule.component.html create mode 100644 ui-ngx/src/app/modules/home/pages/alarm/alarm-rule.component.ts create mode 100644 ui-ngx/src/app/modules/home/pages/alarm/alarm-rules-table-config.resolver.ts create mode 100644 ui-ngx/src/app/modules/home/pages/alarm/alarm.module.ts create mode 100644 ui-ngx/src/app/modules/home/pages/entities/entities-routing.module.ts create mode 100644 ui-ngx/src/app/modules/home/pages/entities/entities.module.ts create mode 100644 ui-ngx/src/app/modules/home/pages/features/features-routing.module.ts create mode 100644 ui-ngx/src/app/modules/home/pages/features/features.module.ts create mode 100644 ui-ngx/src/app/modules/home/pages/notification/notification-routing.module.ts create mode 100644 ui-ngx/src/app/modules/home/pages/notification/notification.module.ts create mode 100644 ui-ngx/src/app/shared/models/alarm-rule.models.ts create mode 100644 ui-ngx/src/app/shared/models/id/alarm-rule-id.ts diff --git a/ui-ngx/src/app/core/http/alarm.service.ts b/ui-ngx/src/app/core/http/alarm.service.ts index 802045b684..a30f3169db 100644 --- a/ui-ngx/src/app/core/http/alarm.service.ts +++ b/ui-ngx/src/app/core/http/alarm.service.ts @@ -70,6 +70,12 @@ export class AlarmService { defaultHttpOptionsFromConfig(config)); } + public getAllAlarms(query: AlarmQuery, + config?: RequestConfig): Observable> { + return this.http.get>(`/api/alarms${query.toQuery()}`, + defaultHttpOptionsFromConfig(config)); + } + public getHighestAlarmSeverity(entityId: EntityId, alarmSearchStatus: AlarmSearchStatus, alarmStatus: AlarmStatus, config?: RequestConfig): Observable { let url = `/api/alarm/highestSeverity/${entityId.entityType}/${entityId.id}`; diff --git a/ui-ngx/src/app/core/services/menu.service.ts b/ui-ngx/src/app/core/services/menu.service.ts index 4d2ea4f4e9..f8b946cbed 100644 --- a/ui-ngx/src/app/core/services/menu.service.ts +++ b/ui-ngx/src/app/core/services/menu.service.ts @@ -19,19 +19,20 @@ import { AuthService } from '../auth/auth.service'; import { select, Store } from '@ngrx/store'; import { AppState } from '../core.state'; import { getCurrentOpenedMenuSections, selectAuth, selectIsAuthenticated } from '../auth/auth.selectors'; -import { take } from 'rxjs/operators'; +import { filter, take } from 'rxjs/operators'; import { HomeSection, MenuSection } from '@core/services/menu.models'; import { BehaviorSubject, Observable, Subject } from 'rxjs'; import { Authority } from '@shared/models/authority.enum'; import { guid } from '@core/utils'; import { AuthState } from '@core/auth/auth.models'; -import { Router } from '@angular/router'; +import { NavigationEnd, Router } from '@angular/router'; @Injectable({ providedIn: 'root' }) export class MenuService { + currentMenuSections: Array; menuSections$: Subject> = new BehaviorSubject>([]); homeSections$: Subject> = new BehaviorSubject>([]); @@ -45,41 +46,49 @@ export class MenuService { } } ); + this.router.events.pipe(filter(event => event instanceof NavigationEnd)).subscribe( + () => { + this.updateOpenedMenuSections(); + } + ); } private buildMenu() { this.store.pipe(select(selectAuth), take(1)).subscribe( (authState: AuthState) => { if (authState.authUser) { - let menuSections: Array; let homeSections: Array; switch (authState.authUser.authority) { case Authority.SYS_ADMIN: - menuSections = this.buildSysAdminMenu(authState); + this.currentMenuSections = this.buildSysAdminMenu(authState); homeSections = this.buildSysAdminHome(authState); break; case Authority.TENANT_ADMIN: - menuSections = this.buildTenantAdminMenu(authState); + this.currentMenuSections = this.buildTenantAdminMenu(authState); homeSections = this.buildTenantAdminHome(authState); break; case Authority.CUSTOMER_USER: - menuSections = this.buildCustomerUserMenu(authState); + this.currentMenuSections = this.buildCustomerUserMenu(authState); homeSections = this.buildCustomerUserHome(authState); break; } - const url = this.router.url; - const openedMenuSections = getCurrentOpenedMenuSections(this.store); - menuSections.filter(section => section.type === 'toggle' && - (url.startsWith(section.path) || openedMenuSections.includes(section.path))).forEach( - section => section.opened = true - ); - this.menuSections$.next(menuSections); + this.updateOpenedMenuSections(); + this.menuSections$.next(this.currentMenuSections); this.homeSections$.next(homeSections); } } ); } + private updateOpenedMenuSections() { + const url = this.router.url; + const openedMenuSections = getCurrentOpenedMenuSections(this.store); + this.currentMenuSections.filter(section => section.type === 'toggle' && + (url.startsWith(section.path) || openedMenuSections.includes(section.path))).forEach( + section => section.opened = true + ); + } + private buildSysAdminMenu(authState: AuthState): Array { const sections: Array = []; sections.push( @@ -132,7 +141,7 @@ export class MenuService { }, { id: guid(), - name: 'admin.system-settings', + name: 'admin.settings', type: 'link', path: '/settings', icon: 'settings', @@ -295,31 +304,64 @@ export class MenuService { }, { id: guid(), - name: 'rulechain.rulechains', + name: 'alarm.alarms', type: 'link', - path: '/ruleChains', - icon: 'settings_ethernet' - }, - { - id: guid(), - name: 'customer.customers', - type: 'link', - path: '/customers', - icon: 'supervisor_account' + path: '/alarm', + icon: 'notifications', + pages: [ + { + id: guid(), + name: 'alarm.all-alarms', + type: 'link', + path: '/alarm/alarms', + icon: 'notifications' + }, + { + id: guid(), + name: 'alarm-rule.rules', + type: 'link', + path: '/alarm/rules', + icon: 'edit_notifications' + } + ] }, { id: guid(), - name: 'asset.assets', + name: 'dashboard.dashboards', type: 'link', - path: '/assets', - icon: 'domain' + path: '/dashboards', + icon: 'dashboards' }, { id: guid(), - name: 'device.devices', - type: 'link', - path: '/devices', - icon: 'devices_other' + name: 'entity.entities', + type: 'toggle', + path: '/entities', + height: '120px', + icon: 'category', + pages: [ + { + id: guid(), + name: 'device.devices', + type: 'link', + path: '/entities/devices', + icon: 'devices_other' + }, + { + id: guid(), + name: 'asset.assets', + type: 'link', + path: '/entities/assets', + icon: 'domain' + }, + { + id: guid(), + name: 'entity-view.entity-views', + type: 'link', + path: '/entities/entityViews', + icon: 'view_quilt' + } + ] }, { id: guid(), @@ -349,36 +391,29 @@ export class MenuService { }, { id: guid(), - name: 'ota-update.ota-updates', - type: 'link', - path: '/otaUpdates', - icon: 'memory' - }, - { - id: guid(), - name: 'entity-view.entity-views', + name: 'customer.customers', type: 'link', - path: '/entityViews', - icon: 'view_quilt' + path: '/customers', + icon: 'supervisor_account' } ); if (authState.edgesSupportEnabled) { sections.push( - { - id: guid(), - name: 'edge.edge-instances', - type: 'link', - path: '/edgeInstances', - icon: 'router' - }, { id: guid(), name: 'edge.management', type: 'toggle', path: '/edgeManagement', - height: '40px', + height: '80px', icon: 'settings_input_antenna', pages: [ + { + id: guid(), + name: 'edge.instances', + type: 'link', + path: '/edgeManagement/instances', + icon: 'router' + }, { id: guid(), name: 'edge.rulechain-templates', @@ -393,31 +428,106 @@ export class MenuService { sections.push( { id: guid(), - name: 'widget.widget-library', - type: 'link', - path: '/widgets-bundles', - icon: 'now_widgets' - }, - { - id: guid(), - name: 'dashboard.dashboards', - type: 'link', - path: '/dashboards', - icon: 'dashboards' + name: 'feature.advanced-features', + type: 'toggle', + path: '/features', + height: '120px', + icon: 'construction', + pages: [ + { + id: guid(), + name: 'rulechain.rulechains', + type: 'link', + path: '/features/ruleChains', + icon: 'settings_ethernet' + }, + { + id: guid(), + name: 'ota-update.ota-updates', + type: 'link', + path: '/features/otaUpdates', + icon: 'memory' + }, + { + id: guid(), + name: 'version-control.version-control', + type: 'link', + path: '/features/vc', + icon: 'history' + } + ] }, { id: guid(), - name: 'version-control.version-control', - type: 'link', - path: '/vc', - icon: 'history' + name: 'admin.resources', + type: 'toggle', + path: '/resources', + height: '80px', + icon: 'folder', + pages: [ + { + id: guid(), + name: 'widget.widget-library', + type: 'link', + path: '/resources/widgets-bundles', + icon: 'now_widgets' + }, + { + id: guid(), + name: 'resource.resources-library', + type: 'link', + path: '/resources/resources-library', + icon: 'mdi:rhombus-split', + isMdiIcon: true + } + ] }, { id: guid(), - name: 'audit-log.audit-logs', + name: 'notification.notification-center', type: 'link', - path: '/auditLogs', - icon: 'track_changes' + path: '/notification', + icon: 'mdi:message-badge', + isMdiIcon: true, + pages: [ + { + id: guid(), + name: 'notification.inbox', + type: 'link', + path: '/notification/inbox', + icon: 'inbox' + }, + { + id: guid(), + name: 'notification.sent', + type: 'link', + path: '/notification/sent', + icon: 'outbox' + }, + { + id: guid(), + name: 'notification.templates', + type: 'link', + path: '/notification/templates', + icon: 'mdi:message-draw', + isMdiIcon: true + }, + { + id: guid(), + name: 'notification.recipients', + type: 'link', + path: '/notification/recipients', + icon: 'contacts' + }, + { + id: guid(), + name: 'notification.rules', + type: 'link', + path: '/notification/rules', + icon: 'mdi:message-cog', + isMdiIcon: true + } + ] }, { id: guid(), @@ -428,41 +538,49 @@ export class MenuService { }, { id: guid(), - name: 'admin.system-settings', - type: 'toggle', + name: 'admin.settings', + type: 'link', path: '/settings', - height: '160px', icon: 'settings', pages: [ { id: guid(), - name: 'admin.home-settings', + name: 'admin.home', type: 'link', path: '/settings/home', icon: 'settings_applications' }, { id: guid(), - name: 'resource.resources-library', - type: 'link', - path: '/settings/resources-library', - icon: 'folder' - }, - { - id: guid(), - name: 'admin.repository-settings', + name: 'admin.repository', type: 'link', path: '/settings/repository', icon: 'manage_history' }, { id: guid(), - name: 'admin.auto-commit-settings', + name: 'admin.auto-commit', type: 'link', path: '/settings/auto-commit', icon: 'settings_backup_restore' } ] + }, + { + id: guid(), + name: 'security.security', + type: 'link', + path: '/security-settings', + icon: 'security', + pages: [ + { + id: guid(), + name: 'audit-log.audit-logs', + type: 'link', + path: '/auditLogs', + icon: 'track_changes' + } + ] } ); return sections; @@ -640,24 +758,48 @@ export class MenuService { }, { id: guid(), - name: 'asset.assets', + name: 'alarm.alarms', type: 'link', - path: '/assets', - icon: 'domain' + path: '/alarms', + icon: 'notifications' }, { id: guid(), - name: 'device.devices', + name: 'dashboard.dashboards', type: 'link', - path: '/devices', - icon: 'devices_other' + path: '/dashboards', + icon: 'dashboards' }, { id: guid(), - name: 'entity-view.entity-views', - type: 'link', - path: '/entityViews', - icon: 'view_quilt' + name: 'entity.entities', + type: 'toggle', + path: '/entities', + height: '120px', + icon: 'category', + pages: [ + { + id: guid(), + name: 'device.devices', + type: 'link', + path: '/entities/devices', + icon: 'devices_other' + }, + { + id: guid(), + name: 'asset.assets', + type: 'link', + path: '/entities/assets', + icon: 'domain' + }, + { + id: guid(), + name: 'entity-view.entity-views', + type: 'link', + path: '/entities/entityViews', + icon: 'view_quilt' + } + ] } ); if (authState.edgesSupportEnabled) { @@ -666,20 +808,11 @@ export class MenuService { id: guid(), name: 'edge.edge-instances', type: 'link', - path: '/edgeInstances', + path: '/edgeManagement/instances', icon: 'router' } ); } - sections.push( - { - id: guid(), - name: 'dashboard.dashboards', - type: 'link', - path: '/dashboards', - icon: 'dashboard' - } - ); return sections; } diff --git a/ui-ngx/src/app/modules/home/components/alarm/alarm-table-config.ts b/ui-ngx/src/app/modules/home/components/alarm/alarm-table-config.ts index ecac2bbaa5..320158900e 100644 --- a/ui-ngx/src/app/modules/home/components/alarm/alarm-table-config.ts +++ b/ui-ngx/src/app/modules/home/components/alarm/alarm-table-config.ts @@ -33,7 +33,7 @@ import { AlarmQuery, AlarmSearchStatus, alarmSeverityColors, - alarmSeverityTranslations, + alarmSeverityTranslations, AlarmsMode, alarmStatusTranslations } from '@app/shared/models/alarm.models'; import { AlarmService } from '@app/core/http/alarm.service'; @@ -48,6 +48,7 @@ import { Store } from '@ngrx/store'; import { AppState } from '@core/core.state'; import { getCurrentAuthUser } from '@core/auth/auth.selectors'; import { Authority } from '@shared/models/authority.enum'; +import { AuditLogMode } from '@shared/models/audit-log.models'; export class AlarmTableConfig extends EntityTableConfig { @@ -60,14 +61,16 @@ export class AlarmTableConfig extends EntityTableConfig private translate: TranslateService, private datePipe: DatePipe, private dialog: MatDialog, + private alarmsMode: AlarmsMode = AlarmsMode.ALL, public entityId: EntityId = null, private defaultSearchStatus: AlarmSearchStatus = AlarmSearchStatus.ANY, - private store: Store) { + private store: Store, + pageMode = false) { super(); this.loadDataOnInit = false; this.tableTitle = ''; this.useTimePageLink = true; - this.pageMode = false; + this.pageMode = pageMode; this.defaultTimewindowInterval = historyInterval(DAY * 30); this.detailsPanelEnabled = false; this.selectionEnabled = false; @@ -117,7 +120,12 @@ export class AlarmTableConfig extends EntityTableConfig fetchAlarms(pageLink: TimePageLink): Observable> { const query = new AlarmQuery(this.entityId, pageLink, this.searchStatus, null, true); - return this.alarmService.getAlarms(query); + switch (this.alarmsMode) { + case AlarmsMode.ALL: + return this.alarmService.getAllAlarms(query); + case AlarmsMode.ENTITY: + return this.alarmService.getAlarms(query); + } } showAlarmDetails(entity: AlarmInfo) { diff --git a/ui-ngx/src/app/modules/home/components/alarm/alarm-table.component.html b/ui-ngx/src/app/modules/home/components/alarm/alarm-table.component.html index 8bd49854d0..6af642022b 100644 --- a/ui-ngx/src/app/modules/home/components/alarm/alarm-table.component.html +++ b/ui-ngx/src/app/modules/home/components/alarm/alarm-table.component.html @@ -15,4 +15,4 @@ limitations under the License. --> - + diff --git a/ui-ngx/src/app/modules/home/components/alarm/alarm-table.component.scss b/ui-ngx/src/app/modules/home/components/alarm/alarm-table.component.scss index b80526aee8..f6b90c99c4 100644 --- a/ui-ngx/src/app/modules/home/components/alarm/alarm-table.component.scss +++ b/ui-ngx/src/app/modules/home/components/alarm/alarm-table.component.scss @@ -14,7 +14,7 @@ * limitations under the License. */ :host ::ng-deep { - tb-entities-table { + tb-entities-table.tb-details-mode { .mat-drawer-container { background-color: white; } diff --git a/ui-ngx/src/app/modules/home/components/alarm/alarm-table.component.ts b/ui-ngx/src/app/modules/home/components/alarm/alarm-table.component.ts index 0d53eaaf42..11f6c8a335 100644 --- a/ui-ngx/src/app/modules/home/components/alarm/alarm-table.component.ts +++ b/ui-ngx/src/app/modules/home/components/alarm/alarm-table.component.ts @@ -22,10 +22,12 @@ import { EntityId } from '@shared/models/id/entity-id'; import { EntitiesTableComponent } from '@home/components/entity/entities-table.component'; import { DialogService } from '@core/services/dialog.service'; import { AlarmTableConfig } from './alarm-table-config'; -import { AlarmSearchStatus } from '@shared/models/alarm.models'; +import { AlarmSearchStatus, AlarmsMode } from '@shared/models/alarm.models'; import { AlarmService } from '@app/core/http/alarm.service'; import { Store } from '@ngrx/store'; import { AppState } from '@core/core.state'; +import { ActivatedRoute } from '@angular/router'; +import { isDefinedAndNotNull } from '@core/utils'; @Component({ selector: 'tb-alarm-table', @@ -37,6 +39,8 @@ export class AlarmTableComponent implements OnInit { activeValue = false; dirtyValue = false; entityIdValue: EntityId; + alarmsMode = AlarmsMode.ENTITY; + detailsMode = true; @Input() set active(active: boolean) { @@ -71,20 +75,30 @@ export class AlarmTableComponent implements OnInit { private translate: TranslateService, private datePipe: DatePipe, private dialog: MatDialog, - private store: Store) { + private store: Store, + private route: ActivatedRoute) { } ngOnInit() { this.dirtyValue = !this.activeValue; + const pageMode = !!this.route.snapshot.data.isPage; + if (pageMode) { + this.detailsMode = false; + } + if (isDefinedAndNotNull(this.route.snapshot.data.alarmsMode)) { + this.alarmsMode = this.route.snapshot.data.alarmsMode; + } this.alarmTableConfig = new AlarmTableConfig( this.alarmService, this.dialogService, this.translate, this.datePipe, this.dialog, + this.alarmsMode, this.entityIdValue, AlarmSearchStatus.ANY, - this.store + this.store, + pageMode ); } diff --git a/ui-ngx/src/app/modules/home/components/profile/alarm/alarm-rule.component.ts b/ui-ngx/src/app/modules/home/components/profile/alarm/alarm-rule.component.ts index de83ab4dcb..404d29804b 100644 --- a/ui-ngx/src/app/modules/home/components/profile/alarm/alarm-rule.component.ts +++ b/ui-ngx/src/app/modules/home/components/profile/alarm/alarm-rule.component.ts @@ -25,7 +25,7 @@ import { Validator, Validators } from '@angular/forms'; -import { AlarmRule } from '@shared/models/device.models'; +import { DeviceProfileAlarmRule } from '@shared/models/device.models'; import { MatDialog } from '@angular/material/dialog'; import { coerceBooleanProperty } from '@angular/cdk/coercion'; import { isDefinedAndNotNull } from '@core/utils'; @@ -71,7 +71,7 @@ export class AlarmRuleComponent implements ControlValueAccessor, OnInit, Validat @Input() deviceProfileId: EntityId; - private modelValue: AlarmRule; + private modelValue: DeviceProfileAlarmRule; alarmRuleFormGroup: UntypedFormGroup; @@ -112,7 +112,7 @@ export class AlarmRuleComponent implements ControlValueAccessor, OnInit, Validat } } - writeValue(value: AlarmRule): void { + writeValue(value: DeviceProfileAlarmRule): void { this.modelValue = value; const model = this.modelValue ? { ...this.modelValue, diff --git a/ui-ngx/src/app/modules/home/components/profile/alarm/create-alarm-rules.component.ts b/ui-ngx/src/app/modules/home/components/profile/alarm/create-alarm-rules.component.ts index 28f9db1c09..40cc7e2ec6 100644 --- a/ui-ngx/src/app/modules/home/components/profile/alarm/create-alarm-rules.component.ts +++ b/ui-ngx/src/app/modules/home/components/profile/alarm/create-alarm-rules.component.ts @@ -27,7 +27,7 @@ import { Validator, Validators } from '@angular/forms'; -import { AlarmRule, alarmRuleValidator } from '@shared/models/device.models'; +import { DeviceProfileAlarmRule, alarmRuleValidator } from '@shared/models/device.models'; import { MatDialog } from '@angular/material/dialog'; import { Subscription } from 'rxjs'; import { AlarmSeverity, alarmSeverityTranslations } from '@shared/models/alarm.models'; @@ -101,7 +101,7 @@ export class CreateAlarmRulesComponent implements ControlValueAccessor, OnInit, } } - writeValue(createAlarmRules: {[severity: string]: AlarmRule}): void { + writeValue(createAlarmRules: {[severity: string]: DeviceProfileAlarmRule}): void { if (this.valueChangeSubscription) { this.valueChangeSubscription.unsubscribe(); } @@ -138,7 +138,7 @@ export class CreateAlarmRulesComponent implements ControlValueAccessor, OnInit, } public addCreateAlarmRule() { - const createAlarmRule: AlarmRule = { + const createAlarmRule: DeviceProfileAlarmRule = { condition: { condition: [] } @@ -179,15 +179,15 @@ export class CreateAlarmRulesComponent implements ControlValueAccessor, OnInit, private updateUsedSeverities() { this.usedSeverities = []; - const value: {severity: string, alarmRule: AlarmRule}[] = this.createAlarmRulesFormGroup.get('createAlarmRules').value; + const value: {severity: string, alarmRule: DeviceProfileAlarmRule}[] = this.createAlarmRulesFormGroup.get('createAlarmRules').value; value.forEach((rule, index) => { this.usedSeverities[index] = AlarmSeverity[rule.severity]; }); } private updateModel() { - const value: {severity: string, alarmRule: AlarmRule}[] = this.createAlarmRulesFormGroup.get('createAlarmRules').value; - const createAlarmRules: {[severity: string]: AlarmRule} = {}; + const value: {severity: string, alarmRule: DeviceProfileAlarmRule}[] = this.createAlarmRulesFormGroup.get('createAlarmRules').value; + const createAlarmRules: {[severity: string]: DeviceProfileAlarmRule} = {}; value.forEach(v => { createAlarmRules[v.severity] = v.alarmRule; }); diff --git a/ui-ngx/src/app/modules/home/components/profile/alarm/device-profile-alarm.component.ts b/ui-ngx/src/app/modules/home/components/profile/alarm/device-profile-alarm.component.ts index 984fa5746f..1f2f1aba61 100644 --- a/ui-ngx/src/app/modules/home/components/profile/alarm/device-profile-alarm.component.ts +++ b/ui-ngx/src/app/modules/home/components/profile/alarm/device-profile-alarm.component.ts @@ -25,7 +25,7 @@ import { Validator, Validators } from '@angular/forms'; -import { AlarmRule, DeviceProfileAlarm, deviceProfileAlarmValidator } from '@shared/models/device.models'; +import { DeviceProfileAlarmRule, DeviceProfileAlarm, deviceProfileAlarmValidator } from '@shared/models/device.models'; import { MatDialog } from '@angular/material/dialog'; import { COMMA, ENTER, SEMICOLON } from '@angular/cdk/keycodes'; import { MatChipInputEvent } from '@angular/material/chips'; @@ -128,7 +128,7 @@ export class DeviceProfileAlarmComponent implements ControlValueAccessor, OnInit } public addClearAlarmRule() { - const clearAlarmRule: AlarmRule = { + const clearAlarmRule: DeviceProfileAlarmRule = { condition: { condition: [] } 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 252acf0156..bf8302d9c6 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 @@ -36,16 +36,9 @@ import { QueuesTableConfigResolver } from '@home/pages/admin/queue/queues-table- import { RepositoryAdminSettingsComponent } from '@home/pages/admin/repository-admin-settings.component'; import { AutoCommitAdminSettingsComponent } from '@home/pages/admin/auto-commit-admin-settings.component'; import { TwoFactorAuthSettingsComponent } from '@home/pages/admin/two-factor-auth-settings.component'; -import { WidgetsBundlesTableConfigResolver } from '@home/pages/widget/widgets-bundles-table-config.resolver'; -import { - WidgetEditorAddDataResolver, widgetEditorBreadcumbLabelFunction, - WidgetEditorDataResolver, - WidgetsBundleResolver, - WidgetsTypesDataResolver, widgetTypesBreadcumbLabelFunction -} from '@home/pages/widget/widget-library-routing.module'; -import { WidgetLibraryComponent } from '@home/pages/widget/widget-library.component'; -import { WidgetEditorComponent } from '@home/pages/widget/widget-editor.component'; +import { widgetsBundlesRoutes } from '@home/pages/widget/widget-library-routing.module'; import { RouterTabsComponent } from '@home/components/router-tabs.component'; +import { auditLogsRoutes } from '@home/pages/audit-log/audit-log-routing.module'; @Injectable() export class OAuth2LoginProcessingUrlResolver implements Resolve { @@ -77,85 +70,7 @@ const routes: Routes = [ redirectTo: '/resources/widgets-bundles' } }, - { - path: 'widgets-bundles', - data: { - breadcrumb: { - label: 'widgets-bundle.widgets-bundles', - icon: 'now_widgets' - } - }, - children: [ - { - path: '', - component: EntitiesTableComponent, - data: { - auth: [Authority.SYS_ADMIN, Authority.TENANT_ADMIN], - title: 'widgets-bundle.widgets-bundles' - }, - resolve: { - entitiesTableConfig: WidgetsBundlesTableConfigResolver - } - }, - { - path: ':widgetsBundleId/widgetTypes', - data: { - breadcrumb: { - labelFunction: widgetTypesBreadcumbLabelFunction, - icon: 'now_widgets' - } as BreadCrumbConfig - }, - resolve: { - widgetsBundle: WidgetsBundleResolver - }, - children: [ - { - path: '', - component: WidgetLibraryComponent, - data: { - auth: [Authority.SYS_ADMIN, Authority.TENANT_ADMIN], - title: 'widget.widget-library' - }, - resolve: { - widgetsData: WidgetsTypesDataResolver - } - }, - { - path: ':widgetTypeId', - component: WidgetEditorComponent, - canDeactivate: [ConfirmOnExitGuard], - data: { - auth: [Authority.SYS_ADMIN, Authority.TENANT_ADMIN], - title: 'widget.editor', - breadcrumb: { - labelFunction: widgetEditorBreadcumbLabelFunction, - icon: 'insert_chart' - } as BreadCrumbConfig - }, - resolve: { - widgetEditorData: WidgetEditorDataResolver - } - }, - { - path: 'add/:widgetType', - component: WidgetEditorComponent, - canDeactivate: [ConfirmOnExitGuard], - data: { - auth: [Authority.SYS_ADMIN, Authority.TENANT_ADMIN], - title: 'widget.editor', - breadcrumb: { - labelFunction: widgetEditorBreadcumbLabelFunction, - icon: 'insert_chart' - } as BreadCrumbConfig - }, - resolve: { - widgetEditorData: WidgetEditorAddDataResolver - } - } - ] - } - ] - }, + ...widgetsBundlesRoutes, { path: 'resources-library', data: { @@ -202,7 +117,7 @@ const routes: Routes = [ data: { auth: [Authority.SYS_ADMIN, Authority.TENANT_ADMIN], breadcrumb: { - label: 'admin.system-settings', + label: 'admin.settings', icon: 'settings' } }, @@ -303,7 +218,7 @@ const routes: Routes = [ auth: [Authority.TENANT_ADMIN], title: 'admin.home-settings', breadcrumb: { - label: 'admin.home-settings', + label: 'admin.home', icon: 'settings_applications' } } @@ -316,7 +231,7 @@ const routes: Routes = [ auth: [Authority.TENANT_ADMIN], title: 'admin.repository-settings', breadcrumb: { - label: 'admin.repository-settings', + label: 'admin.repository', icon: 'manage_history' } } @@ -329,7 +244,7 @@ const routes: Routes = [ auth: [Authority.TENANT_ADMIN], title: 'admin.auto-commit-settings', breadcrumb: { - label: 'admin.auto-commit-settings', + label: 'admin.auto-commit', icon: 'settings_backup_restore' } } @@ -379,7 +294,7 @@ const routes: Routes = [ auth: [Authority.SYS_ADMIN, Authority.TENANT_ADMIN], redirectTo: { SYS_ADMIN: '/security-settings/general', - TENANT_ADMIN: '/security-settings/audit-logs' + TENANT_ADMIN: '/security-settings/auditLogs' } } }, @@ -425,7 +340,8 @@ const routes: Routes = [ resolve: { loginProcessingUrl: OAuth2LoginProcessingUrlResolver } - } + }, + ...auditLogsRoutes ] } ]; @@ -436,12 +352,7 @@ const routes: Routes = [ providers: [ OAuth2LoginProcessingUrlResolver, ResourcesLibraryTableConfigResolver, - QueuesTableConfigResolver, - WidgetsBundlesTableConfigResolver, - WidgetsBundleResolver, - WidgetsTypesDataResolver, - WidgetEditorDataResolver, - WidgetEditorAddDataResolver + QueuesTableConfigResolver ] }) export class AdminRoutingModule { } 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 new file mode 100644 index 0000000000..b94d2ec466 --- /dev/null +++ b/ui-ngx/src/app/modules/home/pages/alarm/alarm-routing.module.ts @@ -0,0 +1,140 @@ +/// +/// Copyright © 2016-2023 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 { Injectable, NgModule } from '@angular/core'; +import { Resolve, RouterModule, Routes } from '@angular/router'; +import { ConfirmOnExitGuard } from '@core/guards/confirm-on-exit.guard'; +import { Authority } from '@shared/models/authority.enum'; +import { Observable } from 'rxjs'; +import { OAuth2Service } from '@core/http/oauth2.service'; +import { EntitiesTableComponent } from '@home/components/entity/entities-table.component'; +import { EntityDetailsPageComponent } from '@home/components/entity/entity-details-page.component'; +import { entityDetailsPageBreadcrumbLabelFunction } from '@home/pages/home-pages.models'; +import { BreadCrumbConfig } from '@shared/components/breadcrumb'; +import { RouterTabsComponent } from '@home/components/router-tabs.component'; +import { AlarmTableComponent } from '@home/components/alarm/alarm-table.component'; +import { AlarmRulesTableConfigResolver } from '@home/pages/alarm/alarm-rules-table-config.resolver'; +import { AlarmsMode } from '@shared/models/alarm.models'; + +@Injectable() +export class OAuth2LoginProcessingUrlResolver implements Resolve { + + constructor(private oauth2Service: OAuth2Service) { + } + + resolve(): Observable { + return this.oauth2Service.getLoginProcessingUrl(); + } +} + +const routes: Routes = [ + { + path: 'alarm', + component: RouterTabsComponent, + data: { + auth: [Authority.TENANT_ADMIN], + breadcrumb: { + label: 'alarm.alarms', + icon: 'notifications' + } + }, + children: [ + { + path: '', + children: [], + data: { + auth: [Authority.TENANT_ADMIN], + redirectTo: '/alarm/alarms' + } + }, + { + path: 'alarms', + component: AlarmTableComponent, + data: { + auth: [Authority.TENANT_ADMIN], + title: 'alarm.all-alarms', + breadcrumb: { + label: 'alarm.all-alarms', + icon: 'notifications' + }, + isPage: true, + alarmsMode: AlarmsMode.ALL + } + }, + { + path: 'rules', + data: { + breadcrumb: { + label: 'alarm-rule.rules', + icon: 'edit_notifications' + } + }, + children: [ + { + path: '', + component: EntitiesTableComponent, + data: { + auth: [Authority.TENANT_ADMIN], + title: 'alarm-rule.alarm-rules' + }, + resolve: { + entitiesTableConfig: AlarmRulesTableConfigResolver + } + }, + { + path: ':entityId', + component: EntityDetailsPageComponent, + canDeactivate: [ConfirmOnExitGuard], + data: { + breadcrumb: { + labelFunction: entityDetailsPageBreadcrumbLabelFunction, + icon: 'domain' + } as BreadCrumbConfig, + auth: [Authority.TENANT_ADMIN], + title: 'alarm-rule.alarm-rules' + }, + resolve: { + entitiesTableConfig: AlarmRulesTableConfigResolver + } + } + ] + } + ] + }, + { + path: 'alarms', + component: AlarmTableComponent, + data: { + auth: [Authority.CUSTOMER_USER], + title: 'alarm.alarms', + breadcrumb: { + label: 'alarm.alarms', + icon: 'notifications' + }, + isPage: true, + alarmsMode: AlarmsMode.ALL + } + } +]; + +@NgModule({ + imports: [RouterModule.forChild(routes)], + exports: [RouterModule], + providers: [ + AlarmRulesTableConfigResolver + ] +}) +export class AlarmRoutingModule { } diff --git a/ui-ngx/src/app/modules/home/pages/alarm/alarm-rule-tabs.component.html b/ui-ngx/src/app/modules/home/pages/alarm/alarm-rule-tabs.component.html new file mode 100644 index 0000000000..4caaa3b523 --- /dev/null +++ b/ui-ngx/src/app/modules/home/pages/alarm/alarm-rule-tabs.component.html @@ -0,0 +1,21 @@ + + + + diff --git a/ui-ngx/src/app/modules/home/pages/alarm/alarm-rule-tabs.component.ts b/ui-ngx/src/app/modules/home/pages/alarm/alarm-rule-tabs.component.ts new file mode 100644 index 0000000000..a65cf5e28a --- /dev/null +++ b/ui-ngx/src/app/modules/home/pages/alarm/alarm-rule-tabs.component.ts @@ -0,0 +1,38 @@ +/// +/// Copyright © 2016-2023 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 { Component } from '@angular/core'; +import { Store } from '@ngrx/store'; +import { AppState } from '@core/core.state'; +import { EntityTabsComponent } from '../../components/entity/entity-tabs.component'; +import { AlarmRule } from '@shared/models/alarm-rule.models'; + +@Component({ + selector: 'tb-alarm-rule-tabs', + templateUrl: './alarm-rule-tabs.component.html', + styleUrls: [] +}) +export class AlarmRuleTabsComponent extends EntityTabsComponent { + + constructor(protected store: Store) { + super(store); + } + + ngOnInit() { + super.ngOnInit(); + } + +} diff --git a/ui-ngx/src/app/modules/home/pages/alarm/alarm-rule.component.html b/ui-ngx/src/app/modules/home/pages/alarm/alarm-rule.component.html new file mode 100644 index 0000000000..ea4db3de82 --- /dev/null +++ b/ui-ngx/src/app/modules/home/pages/alarm/alarm-rule.component.html @@ -0,0 +1,47 @@ + +
+ + +
+
+
+
+ + alarm-rule.name + + + {{ 'alarm-rule.name-required' | translate }} + + + {{ 'alarm-rule.name-max-length' | translate }} + + +
+
+
diff --git a/ui-ngx/src/app/modules/home/pages/alarm/alarm-rule.component.ts b/ui-ngx/src/app/modules/home/pages/alarm/alarm-rule.component.ts new file mode 100644 index 0000000000..1f1131a105 --- /dev/null +++ b/ui-ngx/src/app/modules/home/pages/alarm/alarm-rule.component.ts @@ -0,0 +1,63 @@ +/// +/// Copyright © 2016-2023 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 { ChangeDetectorRef, Component, Inject, Optional } from '@angular/core'; +import { Store } from '@ngrx/store'; +import { AppState } from '@core/core.state'; +import { UntypedFormBuilder, UntypedFormGroup, Validators } from '@angular/forms'; +import { TranslateService } from '@ngx-translate/core'; +import { EntityTableConfig } from '@home/models/entity/entities-table-config.models'; +import { EntityComponent } from '@home/components/entity/entity.component'; +import { AlarmRule } from '@shared/models/alarm-rule.models'; + +@Component({ + selector: 'tb-alarm-rule', + templateUrl: './alarm-rule.component.html', + styleUrls: [] +}) +export class AlarmRuleComponent extends EntityComponent { + + constructor(protected store: Store, + protected translate: TranslateService, + @Optional() @Inject('entity') protected entityValue: AlarmRule, + @Optional() @Inject('entitiesTableConfig') protected entitiesTableConfigValue: EntityTableConfig, + protected fb: UntypedFormBuilder, + protected cd: ChangeDetectorRef) { + super(store, fb, entityValue, entitiesTableConfigValue, cd); + } + + hideDelete() { + if (this.entitiesTableConfig) { + return !this.entitiesTableConfig.deleteEnabled(this.entity); + } else { + return false; + } + } + + buildForm(entity: AlarmRule): UntypedFormGroup { + const form = this.fb.group( + { + name: [entity ? entity.name : '', [Validators.required, Validators.maxLength(255)]] + } + ); + return form; + } + + updateForm(entity: AlarmRule) { + this.entityForm.patchValue({name: entity.name}); + } + +} diff --git a/ui-ngx/src/app/modules/home/pages/alarm/alarm-rules-table-config.resolver.ts b/ui-ngx/src/app/modules/home/pages/alarm/alarm-rules-table-config.resolver.ts new file mode 100644 index 0000000000..2bfb9a3c9c --- /dev/null +++ b/ui-ngx/src/app/modules/home/pages/alarm/alarm-rules-table-config.resolver.ts @@ -0,0 +1,100 @@ +/// +/// Copyright © 2016-2023 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 { Injectable } from '@angular/core'; +import { Resolve, Router } from '@angular/router'; +import { + DateEntityTableColumn, + EntityTableColumn, + EntityTableConfig +} from '@home/models/entity/entities-table-config.models'; +import { TranslateService } from '@ngx-translate/core'; +import { DatePipe } from '@angular/common'; +import { EntityType, entityTypeResources, entityTypeTranslations } from '@shared/models/entity-type.models'; +import { EntityAction } from '@home/models/entity/entity-component.models'; +import { DialogService } from '@core/services/dialog.service'; +import { MatDialog } from '@angular/material/dialog'; +import { ImportExportService } from '@home/components/import-export/import-export.service'; +import { HomeDialogsService } from '@home/dialogs/home-dialogs.service'; +import { AlarmRuleComponent } from '@home/pages/alarm/alarm-rule.component'; +import { AlarmRuleTabsComponent } from '@home/pages/alarm/alarm-rule-tabs.component'; +import { AlarmRule } from '@shared/models/alarm-rule.models'; +import { emptyPageData } from '@shared/models/page/page-data'; +import { of } from 'rxjs'; + +@Injectable() +export class AlarmRulesTableConfigResolver implements Resolve> { + + private readonly config: EntityTableConfig = new EntityTableConfig(); + + constructor(private importExport: ImportExportService, + private homeDialogs: HomeDialogsService, + private translate: TranslateService, + private datePipe: DatePipe, + private dialogService: DialogService, + private router: Router, + private dialog: MatDialog) { + + this.config.entityType = EntityType.ALARM_RULE; + this.config.entityComponent = AlarmRuleComponent; + this.config.entityTabsComponent = AlarmRuleTabsComponent; + this.config.entityTranslations = entityTypeTranslations.get(EntityType.ALARM_RULE); + this.config.entityResources = entityTypeResources.get(EntityType.ALARM_RULE); + + this.config.hideDetailsTabsOnEdit = false; + + this.config.columns.push( + new DateEntityTableColumn('createdTime', 'common.created-time', this.datePipe, '150px'), + new EntityTableColumn('name', 'alarm-rule.name', '50%') + ); + + this.config.deleteEntityTitle = alarmRule => this.translate.instant('alarm-rule.delete-alarm-rule-title', + { alarmRuleName: alarmRule.name }); + this.config.deleteEntityContent = () => this.translate.instant('alarm-rule.delete-alarm-rule-text'); + this.config.deleteEntitiesTitle = count => this.translate.instant('alarm-rule.delete-alarm-rules-title', {count}); + this.config.deleteEntitiesContent = () => this.translate.instant('alarm-rule.delete-alarm-rules-text'); + + this.config.entitiesFetchFunction = pageLink => of(emptyPageData()); // TODO + this.config.loadEntity = id => of(null); // TODO + this.config.saveEntity = alarmRule => of(alarmRule); // TODO + this.config.deleteEntity = id => of(null); // TODO + this.config.onEntityAction = action => this.onAlarmRuleAction(action); + } + + resolve(): EntityTableConfig { + this.config.tableTitle = this.translate.instant('alarm-rule.alarm-rules'); + + return this.config; + } + + private openAlarmRule($event: Event, alarmRule: AlarmRule) { + if ($event) { + $event.stopPropagation(); + } + const url = this.router.createUrlTree(['alarm', 'rules', alarmRule.id.id]); + this.router.navigateByUrl(url); + } + + onAlarmRuleAction(action: EntityAction): boolean { + switch (action.action) { + case 'open': + this.openAlarmRule(action.event, action.entity); + return true; + } + return false; + } + +} diff --git a/ui-ngx/src/app/modules/home/pages/alarm/alarm.module.ts b/ui-ngx/src/app/modules/home/pages/alarm/alarm.module.ts new file mode 100644 index 0000000000..6c06e3ffda --- /dev/null +++ b/ui-ngx/src/app/modules/home/pages/alarm/alarm.module.ts @@ -0,0 +1,39 @@ +/// +/// Copyright © 2016-2023 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 { NgModule } from '@angular/core'; +import { CommonModule } from '@angular/common'; +import { SharedModule } from '@shared/shared.module'; +import { HomeDialogsModule } from '../../dialogs/home-dialogs.module'; +import { HomeComponentsModule } from '@modules/home/components/home-components.module'; +import { AlarmRoutingModule } from '@home/pages/alarm/alarm-routing.module'; +import { AlarmRuleComponent } from '@home/pages/alarm/alarm-rule.component'; +import { AlarmRuleTabsComponent } from '@home/pages/alarm/alarm-rule-tabs.component'; + +@NgModule({ + declarations: [ + AlarmRuleComponent, + AlarmRuleTabsComponent + ], + imports: [ + CommonModule, + SharedModule, + HomeComponentsModule, + HomeDialogsModule, + AlarmRoutingModule + ] +}) +export class AlarmModule { } 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 b93125758a..1821acbf6c 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 @@ -25,7 +25,7 @@ import { BreadCrumbConfig } from '@shared/components/breadcrumb'; import { ConfirmOnExitGuard } from '@core/guards/confirm-on-exit.guard'; import { entityDetailsPageBreadcrumbLabelFunction } from '@home/pages/home-pages.models'; -const routes: Routes = [ +export const assetRoutes: Routes = [ { path: 'assets', data: { @@ -68,6 +68,18 @@ const routes: Routes = [ } ]; +const routes: Routes = [ + { + path: 'assets', + pathMatch: 'full', + redirectTo: '/entities/assets' + }, + { + path: 'assets/:entityId', + redirectTo: '/entities/assets/:entityId' + } +]; + @NgModule({ imports: [RouterModule.forChild(routes)], exports: [RouterModule], 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 17e5b8c84f..557311181a 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 @@ -19,7 +19,7 @@ import { RouterModule, Routes } from '@angular/router'; import { Authority } from '@shared/models/authority.enum'; import { AuditLogTableComponent } from '@home/components/audit-log/audit-log-table.component'; -const routes: Routes = [ +export const auditLogsRoutes: Routes = [ { path: 'auditLogs', component: AuditLogTableComponent, @@ -35,8 +35,16 @@ const routes: Routes = [ } ]; +const routes: Routes = [ + { + path: 'auditLogs', + redirectTo: '/security-settings/auditLogs' + } +]; + @NgModule({ imports: [RouterModule.forChild(routes)], - exports: [RouterModule] + exports: [RouterModule], + providers: [] }) export class AuditLogRoutingModule { } 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 ac29871576..220f9fd623 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 @@ -24,8 +24,10 @@ 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 { assetRoutes } from '@home/pages/asset/asset-routing.module'; +import { entityViewRoutes } from '@home/pages/entity-view/entity-view-routing.module'; -const routes: Routes = [ +export const deviceRoutes: Routes = [ { path: 'devices', data: { @@ -68,6 +70,18 @@ const routes: Routes = [ } ]; +const routes: Routes = [ + { + path: 'devices', + pathMatch: 'full', + redirectTo: '/entities/devices' + }, + { + path: 'devices/:entityId', + redirectTo: '/entities/devices/:entityId' + } +]; + @NgModule({ imports: [RouterModule.forChild(routes)], exports: [RouterModule], 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 44e0c9c00f..5dcdee3169 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 @@ -44,49 +44,28 @@ import { entityDetailsPageBreadcrumbLabelFunction } from '@home/pages/home-pages const routes: Routes = [ { - path: 'edgeInstances', + path: 'edgeManagement', data: { breadcrumb: { - label: 'edge.edge-instances', - icon: 'router' + label: 'edge.management', + icon: 'settings_input_antenna' } }, children: [ { path: '', - component: EntitiesTableComponent, + children: [], data: { auth: [Authority.TENANT_ADMIN, Authority.CUSTOMER_USER], - title: 'edge.edge-instances', - edgesType: 'tenant' - }, - resolve: { - entitiesTableConfig: EdgesTableConfigResolver + redirectTo: '/edgeManagement/instances' } }, { - path: ':entityId', - component: EntityDetailsPageComponent, - canDeactivate: [ConfirmOnExitGuard], + path: 'instances', data: { breadcrumb: { - labelFunction: entityDetailsPageBreadcrumbLabelFunction, + label: 'edge.instances', icon: 'router' - } as BreadCrumbConfig, - auth: [Authority.TENANT_ADMIN, Authority.CUSTOMER_USER], - title: 'edge.edge-instances', - edgesType: 'tenant' - }, - resolve: { - entitiesTableConfig: EdgesTableConfigResolver - } - }, - { - path: ':edgeId/assets', - data: { - breadcrumb: { - label: 'edge.assets', - icon: 'domain' } }, children: [ @@ -95,11 +74,11 @@ const routes: Routes = [ component: EntitiesTableComponent, data: { auth: [Authority.TENANT_ADMIN, Authority.CUSTOMER_USER], - title: 'edge.assets', - assetsType: 'edge' + title: 'edge.edge-instances', + edgesType: 'tenant' }, resolve: { - entitiesTableConfig: AssetsTableConfigResolver + entitiesTableConfig: EdgesTableConfigResolver } }, { @@ -109,198 +88,219 @@ const routes: Routes = [ data: { breadcrumb: { labelFunction: entityDetailsPageBreadcrumbLabelFunction, - icon: 'domain' + icon: 'router' } as BreadCrumbConfig, auth: [Authority.TENANT_ADMIN, Authority.CUSTOMER_USER], - title: 'edge.assets', - assetsType: 'edge' + title: 'edge.edge-instances', + edgesType: 'tenant' }, resolve: { - entitiesTableConfig: AssetsTableConfigResolver - } - } - ] - }, - { - path: ':edgeId/devices', - data: { - breadcrumb: { - label: 'edge.devices', - icon: 'devices_other' - } - }, - children: [ - { - path: '', - component: EntitiesTableComponent, - data: { - auth: [Authority.TENANT_ADMIN, Authority.CUSTOMER_USER], - title: 'edge.devices', - devicesType: 'edge' - }, - resolve: { - entitiesTableConfig: DevicesTableConfigResolver + entitiesTableConfig: EdgesTableConfigResolver } }, { - path: ':entityId', - component: EntityDetailsPageComponent, - canDeactivate: [ConfirmOnExitGuard], + path: ':edgeId/assets', data: { breadcrumb: { - labelFunction: entityDetailsPageBreadcrumbLabelFunction, - icon: 'devices_other' - } as BreadCrumbConfig, - auth: [Authority.TENANT_ADMIN, Authority.CUSTOMER_USER], - title: 'edge.devices', - devicesType: 'edge' - }, - resolve: { - entitiesTableConfig: DevicesTableConfigResolver - } - } - ] - }, - { - path: ':edgeId/entityViews', - data: { - breadcrumb: { - label: 'edge.entity-views', - icon: 'view_quilt' - }, - }, - children: [ - { - path: '', - component: EntitiesTableComponent, - data: { - auth: [Authority.TENANT_ADMIN, Authority.CUSTOMER_USER], - title: 'edge.entity-views', - entityViewsType: 'edge' + label: 'edge.assets', + icon: 'domain' + } }, - resolve: { - entitiesTableConfig: EntityViewsTableConfigResolver - } + children: [ + { + path: '', + component: EntitiesTableComponent, + data: { + auth: [Authority.TENANT_ADMIN, Authority.CUSTOMER_USER], + title: 'edge.assets', + assetsType: 'edge' + }, + resolve: { + entitiesTableConfig: AssetsTableConfigResolver + } + }, + { + path: ':entityId', + component: EntityDetailsPageComponent, + canDeactivate: [ConfirmOnExitGuard], + data: { + breadcrumb: { + labelFunction: entityDetailsPageBreadcrumbLabelFunction, + icon: 'domain' + } as BreadCrumbConfig, + auth: [Authority.TENANT_ADMIN, Authority.CUSTOMER_USER], + title: 'edge.assets', + assetsType: 'edge' + }, + resolve: { + entitiesTableConfig: AssetsTableConfigResolver + } + } + ] }, { - path: ':entityId', - component: EntityDetailsPageComponent, - canDeactivate: [ConfirmOnExitGuard], + path: ':edgeId/devices', data: { breadcrumb: { - labelFunction: entityDetailsPageBreadcrumbLabelFunction, + label: 'edge.devices', icon: 'devices_other' - } as BreadCrumbConfig, - auth: [Authority.TENANT_ADMIN, Authority.CUSTOMER_USER], - title: 'edge.entity-views', - entityViewsType: 'edge' + } }, - resolve: { - entitiesTableConfig: EntityViewsTableConfigResolver - } - } - ] - }, - { - path: ':edgeId/dashboards', - data: { - breadcrumb: { - label: 'edge.dashboards', - icon: 'dashboard' - } - }, - children: [ + children: [ + { + path: '', + component: EntitiesTableComponent, + data: { + auth: [Authority.TENANT_ADMIN, Authority.CUSTOMER_USER], + title: 'edge.devices', + devicesType: 'edge' + }, + resolve: { + entitiesTableConfig: DevicesTableConfigResolver + } + }, + { + path: ':entityId', + component: EntityDetailsPageComponent, + canDeactivate: [ConfirmOnExitGuard], + data: { + breadcrumb: { + labelFunction: entityDetailsPageBreadcrumbLabelFunction, + icon: 'devices_other' + } as BreadCrumbConfig, + auth: [Authority.TENANT_ADMIN, Authority.CUSTOMER_USER], + title: 'edge.devices', + devicesType: 'edge' + }, + resolve: { + entitiesTableConfig: DevicesTableConfigResolver + } + } + ] + }, { - path: '', - component: EntitiesTableComponent, + path: ':edgeId/entityViews', data: { - auth: [Authority.TENANT_ADMIN, Authority.CUSTOMER_USER], - dashboardsType: 'edge' - }, - resolve: { - entitiesTableConfig: DashboardsTableConfigResolver + breadcrumb: { + label: 'edge.entity-views', + icon: 'view_quilt' + }, }, + children: [ + { + path: '', + component: EntitiesTableComponent, + data: { + auth: [Authority.TENANT_ADMIN, Authority.CUSTOMER_USER], + title: 'edge.entity-views', + entityViewsType: 'edge' + }, + resolve: { + entitiesTableConfig: EntityViewsTableConfigResolver + } + }, + { + path: ':entityId', + component: EntityDetailsPageComponent, + canDeactivate: [ConfirmOnExitGuard], + data: { + breadcrumb: { + labelFunction: entityDetailsPageBreadcrumbLabelFunction, + icon: 'devices_other' + } as BreadCrumbConfig, + auth: [Authority.TENANT_ADMIN, Authority.CUSTOMER_USER], + title: 'edge.entity-views', + entityViewsType: 'edge' + }, + resolve: { + entitiesTableConfig: EntityViewsTableConfigResolver + } + } + ] }, { - path: ':dashboardId', - component: DashboardPageComponent, + path: ':edgeId/dashboards', data: { breadcrumb: { - labelFunction: dashboardBreadcumbLabelFunction, + label: 'edge.dashboards', icon: 'dashboard' - } as BreadCrumbConfig, - auth: [Authority.TENANT_ADMIN, Authority.CUSTOMER_USER], - title: 'edge.dashboard', - widgetEditMode: false + } }, - resolve: { - dashboard: DashboardResolver - } - } - ] - }, - { - path: ':edgeId/ruleChains', - data: { - breadcrumb: { - label: 'edge.edge-rulechains', - icon: 'settings_ethernet' - } - }, - children: [ - { - path: '', - component: EntitiesTableComponent, - data: { - auth: [Authority.TENANT_ADMIN], - title: 'edge.rulechains', - ruleChainsType: 'edge' - }, - resolve: { - entitiesTableConfig: RuleChainsTableConfigResolver - } + children: [ + { + path: '', + component: EntitiesTableComponent, + data: { + auth: [Authority.TENANT_ADMIN, Authority.CUSTOMER_USER], + dashboardsType: 'edge' + }, + resolve: { + entitiesTableConfig: DashboardsTableConfigResolver + }, + }, + { + path: ':dashboardId', + component: DashboardPageComponent, + data: { + breadcrumb: { + labelFunction: dashboardBreadcumbLabelFunction, + icon: 'dashboard' + } as BreadCrumbConfig, + auth: [Authority.TENANT_ADMIN, Authority.CUSTOMER_USER], + title: 'edge.dashboard', + widgetEditMode: false + }, + resolve: { + dashboard: DashboardResolver + } + } + ] }, { - path: ':ruleChainId', - component: RuleChainPageComponent, - canDeactivate: [ConfirmOnExitGuard], + path: ':edgeId/ruleChains', data: { breadcrumb: { - labelFunction: ruleChainBreadcumbLabelFunction, + label: 'edge.edge-rulechains', icon: 'settings_ethernet' - } as BreadCrumbConfig, - auth: [Authority.TENANT_ADMIN], - title: 'rulechain.edge-rulechain', - import: false, - ruleChainType: RuleChainType.EDGE + } }, - resolve: { - ruleChain: RuleChainResolver, - ruleChainMetaData: RuleChainMetaDataResolver, - ruleNodeComponents: RuleNodeComponentsResolver, - tooltipster: TooltipsterResolver - } + children: [ + { + path: '', + component: EntitiesTableComponent, + data: { + auth: [Authority.TENANT_ADMIN], + title: 'edge.rulechains', + ruleChainsType: 'edge' + }, + resolve: { + entitiesTableConfig: RuleChainsTableConfigResolver + } + }, + { + path: ':ruleChainId', + component: RuleChainPageComponent, + canDeactivate: [ConfirmOnExitGuard], + data: { + breadcrumb: { + labelFunction: ruleChainBreadcumbLabelFunction, + icon: 'settings_ethernet' + } as BreadCrumbConfig, + auth: [Authority.TENANT_ADMIN], + title: 'rulechain.edge-rulechain', + import: false, + ruleChainType: RuleChainType.EDGE + }, + resolve: { + ruleChain: RuleChainResolver, + ruleChainMetaData: RuleChainMetaDataResolver, + ruleNodeComponents: RuleNodeComponentsResolver, + tooltipster: TooltipsterResolver + } + } + ] } ] - } - ] - }, - { - path: 'edgeManagement', - data: { - breadcrumb: { - label: 'edge.management', - icon: 'settings_input_antenna' - } - }, - children: [ - { - path: '', - children: [], - data: { - auth: [Authority.TENANT_ADMIN, Authority.CUSTOMER_USER], - redirectTo: '/edgeManagement/ruleChains' - } }, { path: 'ruleChains', @@ -367,7 +367,63 @@ const routes: Routes = [ ] } ] - }]; + }, + { + path: 'edgeInstances', + pathMatch: 'full', + redirectTo: '/edgeManagement/instances' + }, + { + path: 'edgeInstances/:entityId', + pathMatch: 'full', + redirectTo: '/edgeManagement/instances/:entityId' + }, + { + path: 'edgeInstances/:edgeId/assets', + pathMatch: 'full', + redirectTo: '/edgeManagement/instances/:edgeId/assets' + }, + { + path: 'edgeInstances/:edgeId/assets/:entityId', + redirectTo: '/edgeManagement/instances/:edgeId/assets/:entityId' + }, + { + path: 'edgeInstances/:edgeId/devices', + pathMatch: 'full', + redirectTo: '/edgeManagement/instances/:edgeId/devices' + }, + { + path: 'edgeInstances/:edgeId/devices/:entityId', + redirectTo: '/edgeManagement/instances/:edgeId/devices/:entityId' + }, + { + path: 'edgeInstances/:edgeId/entityViews', + pathMatch: 'full', + redirectTo: '/edgeManagement/instances/:edgeId/entityViews' + }, + { + path: 'edgeInstances/:edgeId/entityViews/:entityId', + redirectTo: '/edgeManagement/instances/:edgeId/entityViews/:entityId' + }, + { + path: 'edgeInstances/:edgeId/dashboards', + pathMatch: 'full', + redirectTo: '/edgeManagement/instances/:edgeId/dashboards' + }, + { + path: 'edgeInstances/:edgeId/dashboards/:dashboardId', + redirectTo: '/edgeManagement/instances/:edgeId/dashboards/:dashboardId' + }, + { + path: 'edgeInstances/:edgeId/ruleChains', + pathMatch: 'full', + redirectTo: '/edgeManagement/instances/:edgeId/ruleChains' + }, + { + path: 'edgeInstances/:edgeId/ruleChains/:ruleChainId', + redirectTo: '/edgeManagement/instances/:edgeId/ruleChains/:ruleChainId' + } +]; @NgModule({ imports: [RouterModule.forChild(routes)], diff --git a/ui-ngx/src/app/modules/home/pages/edge/edges-table-config.resolver.ts b/ui-ngx/src/app/modules/home/pages/edge/edges-table-config.resolver.ts index fa44be34c3..e73bae7c7a 100644 --- a/ui-ngx/src/app/modules/home/pages/edge/edges-table-config.resolver.ts +++ b/ui-ngx/src/app/modules/home/pages/edge/edges-table-config.resolver.ts @@ -429,7 +429,7 @@ export class EdgesTableConfigResolver implements Resolve) { diff --git a/ui-ngx/src/app/modules/home/pages/entities/entities-routing.module.ts b/ui-ngx/src/app/modules/home/pages/entities/entities-routing.module.ts new file mode 100644 index 0000000000..a62f294767 --- /dev/null +++ b/ui-ngx/src/app/modules/home/pages/entities/entities-routing.module.ts @@ -0,0 +1,54 @@ +/// +/// Copyright © 2016-2023 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 { RouterModule, Routes } from '@angular/router'; +import { Authority } from '@shared/models/authority.enum'; +import { NgModule } from '@angular/core'; +import { deviceRoutes } from '@home/pages/device/device-routing.module'; +import { assetRoutes } from '@home/pages/asset/asset-routing.module'; +import { entityViewRoutes } from '@home/pages/entity-view/entity-view-routing.module'; + +const routes: Routes = [ + { + path: 'entities', + data: { + auth: [Authority.TENANT_ADMIN, Authority.CUSTOMER_USER], + breadcrumb: { + label: 'entity.entities', + icon: 'category' + } + }, + children: [ + { + path: '', + children: [], + data: { + auth: [Authority.TENANT_ADMIN, Authority.CUSTOMER_USER], + redirectTo: '/entities/devices' + } + }, + ...deviceRoutes, + ...assetRoutes, + ...entityViewRoutes + ] + } +]; + +@NgModule({ + imports: [RouterModule.forChild(routes)], + exports: [RouterModule] +}) +export class EntitiesRoutingModule { } diff --git a/ui-ngx/src/app/modules/home/pages/entities/entities.module.ts b/ui-ngx/src/app/modules/home/pages/entities/entities.module.ts new file mode 100644 index 0000000000..8aa4bd21a0 --- /dev/null +++ b/ui-ngx/src/app/modules/home/pages/entities/entities.module.ts @@ -0,0 +1,30 @@ +/// +/// Copyright © 2016-2023 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 { NgModule } from '@angular/core'; +import { CommonModule } from '@angular/common'; +import { SharedModule } from '@shared/shared.module'; +import { EntitiesRoutingModule } from '@home/pages/entities/entities-routing.module'; + +@NgModule({ + declarations: [], + imports: [ + CommonModule, + SharedModule, + EntitiesRoutingModule + ] +}) +export class EntitiesModule { } 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 a083e8fb4b..98a1bc3379 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 @@ -25,7 +25,7 @@ import { ConfirmOnExitGuard } from '@core/guards/confirm-on-exit.guard'; import { entityDetailsPageBreadcrumbLabelFunction } from '@home/pages/home-pages.models'; import { BreadCrumbConfig } from '@shared/components/breadcrumb'; -const routes: Routes = [ +export const entityViewRoutes: Routes = [ { path: 'entityViews', data: { @@ -68,6 +68,18 @@ const routes: Routes = [ } ]; +const routes: Routes = [ + { + path: 'entityViews', + pathMatch: 'full', + redirectTo: '/entities/entityViews' + }, + { + path: 'entityViews/:entityId', + redirectTo: '/entities/entityViews/:entityId' + } +]; + @NgModule({ imports: [RouterModule.forChild(routes)], exports: [RouterModule], 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 new file mode 100644 index 0000000000..4448f8a559 --- /dev/null +++ b/ui-ngx/src/app/modules/home/pages/features/features-routing.module.ts @@ -0,0 +1,54 @@ +/// +/// Copyright © 2016-2023 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 { RouterModule, Routes } from '@angular/router'; +import { Authority } from '@shared/models/authority.enum'; +import { NgModule } from '@angular/core'; +import { ruleChainsRoutes } from '@home/pages/rulechain/rulechain-routing.module'; +import { otaUpdatesRoutes } from '@home/pages/ota-update/ota-update-routing.module'; +import { vcRoutes } from '@home/pages/vc/vc-routing.module'; + +const routes: Routes = [ + { + path: 'features', + data: { + auth: [Authority.TENANT_ADMIN], + breadcrumb: { + label: 'feature.advanced-features', + icon: 'construction' + } + }, + children: [ + { + path: '', + children: [], + data: { + auth: [Authority.TENANT_ADMIN], + redirectTo: '/features/ruleChains' + } + }, + ...ruleChainsRoutes, + ...otaUpdatesRoutes, + ...vcRoutes + ] + } +]; + +@NgModule({ + imports: [RouterModule.forChild(routes)], + exports: [RouterModule] +}) +export class FeaturesRoutingModule { } diff --git a/ui-ngx/src/app/modules/home/pages/features/features.module.ts b/ui-ngx/src/app/modules/home/pages/features/features.module.ts new file mode 100644 index 0000000000..aa3a3d9174 --- /dev/null +++ b/ui-ngx/src/app/modules/home/pages/features/features.module.ts @@ -0,0 +1,30 @@ +/// +/// Copyright © 2016-2023 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 { NgModule } from '@angular/core'; +import { CommonModule } from '@angular/common'; +import { SharedModule } from '@shared/shared.module'; +import { FeaturesRoutingModule } from '@home/pages/features/features-routing.module'; + +@NgModule({ + declarations: [], + imports: [ + CommonModule, + SharedModule, + FeaturesRoutingModule + ] +}) +export class FeaturesModule { } diff --git a/ui-ngx/src/app/modules/home/pages/home-pages.module.ts b/ui-ngx/src/app/modules/home/pages/home-pages.module.ts index 433595e51c..b1f4715c33 100644 --- a/ui-ngx/src/app/modules/home/pages/home-pages.module.ts +++ b/ui-ngx/src/app/modules/home/pages/home-pages.module.ts @@ -38,6 +38,10 @@ import { OtaUpdateModule } from '@home/pages/ota-update/ota-update.module'; import { VcModule } from '@home/pages/vc/vc.module'; import { AssetProfileModule } from '@home/pages/asset-profile/asset-profile.module'; import { ProfilesModule } from '@home/pages/profiles/profiles.module'; +import { AlarmModule } from '@home/pages/alarm/alarm.module'; +import { EntitiesModule } from '@home/pages/entities/entities.module'; +import { FeaturesModule } from '@home/pages/features/features.module'; +import { NotificationModule } from '@home/pages/notification/notification.module'; @NgModule({ exports: [ @@ -50,8 +54,12 @@ import { ProfilesModule } from '@home/pages/profiles/profiles.module'; DeviceProfileModule, AssetProfileModule, ProfilesModule, + EntitiesModule, + FeaturesModule, + NotificationModule, DeviceModule, AssetModule, + AlarmModule, EdgeModule, EntityViewModule, CustomerModule, 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 new file mode 100644 index 0000000000..4101f61411 --- /dev/null +++ b/ui-ngx/src/app/modules/home/pages/notification/notification-routing.module.ts @@ -0,0 +1,135 @@ +/// +/// Copyright © 2016-2023 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 { ActivatedRoute, RouterModule, Routes } from '@angular/router'; +import { Authority } from '@shared/models/authority.enum'; +import { Component, NgModule, OnInit } from '@angular/core'; +import { RouterTabsComponent } from '@home/components/router-tabs.component'; +import { isDefinedAndNotNull } from '@core/utils'; + +@Component({ + selector: 'tb-notification-temp-component', + template: '
{{text}}
', + styleUrls: [] +}) +class NotificationTempComponent implements OnInit { + + text: string; + + constructor(private route: ActivatedRoute) {} + + ngOnInit() { + if (isDefinedAndNotNull(this.route.snapshot.data.text)) { + this.text = this.route.snapshot.data.text; + } + } +} + +const routes: Routes = [ + { + path: 'notification', + component: RouterTabsComponent, + data: { + auth: [Authority.TENANT_ADMIN, Authority.CUSTOMER_USER], + breadcrumb: { + label: 'notification.notification-center', + icon: 'mdi:message-badge' + } + }, + children: [ + { + path: '', + children: [], + data: { + auth: [Authority.TENANT_ADMIN, Authority.CUSTOMER_USER], + redirectTo: '/notification/inbox' + } + }, + { + path: 'inbox', + component: NotificationTempComponent, + data: { + auth: [Authority.TENANT_ADMIN, Authority.CUSTOMER_USER], + title: 'notification.inbox', + breadcrumb: { + label: 'notification.inbox', + icon: 'inbox' + }, + text: 'TODO: Implement inbox' + } + }, + { + path: 'sent', + component: NotificationTempComponent, + data: { + auth: [Authority.TENANT_ADMIN], + title: 'notification.sent', + breadcrumb: { + label: 'notification.sent', + icon: 'outbox' + }, + text: 'TODO: Implement sent' + } + }, + { + path: 'templates', + component: NotificationTempComponent, + data: { + auth: [Authority.TENANT_ADMIN], + title: 'notification.templates', + breadcrumb: { + label: 'notification.templates', + icon: 'mdi:message-draw' + }, + text: 'TODO: Implement templates' + } + }, + { + path: 'recipients', + component: NotificationTempComponent, + data: { + auth: [Authority.TENANT_ADMIN], + title: 'notification.recipients', + breadcrumb: { + label: 'notification.recipients', + icon: 'contacts' + }, + text: 'TODO: Implement recipients' + } + }, + { + path: 'rules', + component: NotificationTempComponent, + data: { + auth: [Authority.TENANT_ADMIN], + title: 'notification.rules', + breadcrumb: { + label: 'notification.rules', + icon: 'mdi:message-cog' + }, + text: 'TODO: Implement rules' + } + } + ] + } +]; + +@NgModule({ + declarations: [NotificationTempComponent], + imports: [RouterModule.forChild(routes)], + exports: [RouterModule] +}) +export class NotificationRoutingModule { } diff --git a/ui-ngx/src/app/modules/home/pages/notification/notification.module.ts b/ui-ngx/src/app/modules/home/pages/notification/notification.module.ts new file mode 100644 index 0000000000..b81855b79d --- /dev/null +++ b/ui-ngx/src/app/modules/home/pages/notification/notification.module.ts @@ -0,0 +1,30 @@ +/// +/// Copyright © 2016-2023 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 { NgModule } from '@angular/core'; +import { CommonModule } from '@angular/common'; +import { SharedModule } from '@shared/shared.module'; +import { NotificationRoutingModule } from '@home/pages/notification/notification-routing.module'; + +@NgModule({ + declarations: [], + imports: [ + CommonModule, + SharedModule, + NotificationRoutingModule + ] +}) +export class NotificationModule { } 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 f83aca1841..2af6910e23 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 @@ -24,7 +24,7 @@ import { ConfirmOnExitGuard } from '@core/guards/confirm-on-exit.guard'; import { entityDetailsPageBreadcrumbLabelFunction } from '@home/pages/home-pages.models'; import { BreadCrumbConfig } from '@shared/components/breadcrumb'; -const routes: Routes = [ +export const otaUpdatesRoutes: Routes = [ { path: 'otaUpdates', data: { @@ -65,6 +65,18 @@ const routes: Routes = [ } ]; +const routes: Routes = [ + { + path: 'otaUpdates', + pathMatch: 'full', + redirectTo: '/features/otaUpdates' + }, + { + path: 'otaUpdates/:entityId', + redirectTo: '/features/otaUpdates/:entityId' + } +]; + @NgModule({ imports: [RouterModule.forChild(routes)], exports: [RouterModule], 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 d8a22c67db..ec2148b576 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 @@ -122,7 +122,7 @@ export const importRuleChainBreadcumbLabelFunction: BreadCrumbLabelFunction = ( export const widgetEditorBreadcumbLabelFunction: BreadCrumbLabelFunction = ((route, translate, component) => component ? component.widget.widgetName : ''); -export const routes: Routes = [ +export const widgetsBundlesRoutes: Routes = [ + { + path: 'widgets-bundles', + data: { + breadcrumb: { + label: 'widgets-bundle.widgets-bundles', + icon: 'now_widgets' + } + }, + children: [ + { + path: '', + component: EntitiesTableComponent, + data: { + auth: [Authority.SYS_ADMIN, Authority.TENANT_ADMIN], + title: 'widgets-bundle.widgets-bundles' + }, + resolve: { + entitiesTableConfig: WidgetsBundlesTableConfigResolver + } + }, + { + path: ':widgetsBundleId/widgetTypes', + data: { + breadcrumb: { + labelFunction: widgetTypesBreadcumbLabelFunction, + icon: 'now_widgets' + } as BreadCrumbConfig + }, + resolve: { + widgetsBundle: WidgetsBundleResolver + }, + children: [ + { + path: '', + component: WidgetLibraryComponent, + data: { + auth: [Authority.SYS_ADMIN, Authority.TENANT_ADMIN], + title: 'widget.widget-library' + }, + resolve: { + widgetsData: WidgetsTypesDataResolver + } + }, + { + path: ':widgetTypeId', + component: WidgetEditorComponent, + canDeactivate: [ConfirmOnExitGuard], + data: { + auth: [Authority.SYS_ADMIN, Authority.TENANT_ADMIN], + title: 'widget.editor', + breadcrumb: { + labelFunction: widgetEditorBreadcumbLabelFunction, + icon: 'insert_chart' + } as BreadCrumbConfig + }, + resolve: { + widgetEditorData: WidgetEditorDataResolver + } + }, + { + path: 'add/:widgetType', + component: WidgetEditorComponent, + canDeactivate: [ConfirmOnExitGuard], + data: { + auth: [Authority.SYS_ADMIN, Authority.TENANT_ADMIN], + title: 'widget.editor', + breadcrumb: { + labelFunction: widgetEditorBreadcumbLabelFunction, + icon: 'insert_chart' + } as BreadCrumbConfig + }, + resolve: { + widgetEditorData: WidgetEditorAddDataResolver + } + } + ] + } + ] + }, +]; + +const routes: Routes = [ { path: 'widgets-bundles', pathMatch: 'full', @@ -146,6 +228,12 @@ export const routes: Routes = [ @NgModule({ imports: [RouterModule.forChild(routes)], exports: [RouterModule], - providers: [] + providers: [ + WidgetsBundlesTableConfigResolver, + WidgetsBundleResolver, + WidgetsTypesDataResolver, + WidgetEditorDataResolver, + WidgetEditorAddDataResolver + ] }) export class WidgetLibraryRoutingModule { } diff --git a/ui-ngx/src/app/shared/models/alarm-rule.models.ts b/ui-ngx/src/app/shared/models/alarm-rule.models.ts new file mode 100644 index 0000000000..688fa629c1 --- /dev/null +++ b/ui-ngx/src/app/shared/models/alarm-rule.models.ts @@ -0,0 +1,24 @@ +/// +/// Copyright © 2016-2023 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 { BaseData } from '@shared/models/base-data'; +import { TenantId } from '@shared/models/id/tenant-id'; +import { AlarmRuleId } from '@shared/models/id/alarm-rule-id'; + +export interface AlarmRule extends BaseData { + tenantId: TenantId; + name: string; +} diff --git a/ui-ngx/src/app/shared/models/alarm.models.ts b/ui-ngx/src/app/shared/models/alarm.models.ts index ed962b2fc1..6783847633 100644 --- a/ui-ngx/src/app/shared/models/alarm.models.ts +++ b/ui-ngx/src/app/shared/models/alarm.models.ts @@ -24,6 +24,11 @@ import { EntityType } from '@shared/models/entity-type.models'; import { CustomerId } from '@shared/models/id/customer-id'; import { TableCellButtonActionDescriptor } from '@home/components/widget/lib/table-widget.models'; +export enum AlarmsMode { + ALL, + ENTITY +} + export enum AlarmSeverity { CRITICAL = 'CRITICAL', MAJOR = 'MAJOR', @@ -218,7 +223,7 @@ export class AlarmQuery { } public toQuery(): string { - let query = `/${this.affectedEntityId.entityType}/${this.affectedEntityId.id}`; + let query = this.affectedEntityId ? `/${this.affectedEntityId.entityType}/${this.affectedEntityId.id}` : ''; query += this.pageLink.toQuery(); if (this.searchStatus) { query += `&searchStatus=${this.searchStatus}`; diff --git a/ui-ngx/src/app/shared/models/device.models.ts b/ui-ngx/src/app/shared/models/device.models.ts index e36aa99117..d5939a60d7 100644 --- a/ui-ngx/src/app/shared/models/device.models.ts +++ b/ui-ngx/src/app/shared/models/device.models.ts @@ -498,13 +498,15 @@ export interface CustomTimeSchedulerItem{ endsOn: number; } -export interface AlarmRule { +interface AlarmRule { condition: AlarmCondition; alarmDetails?: string; dashboardId?: DashboardId; schedule?: AlarmSchedule; } +export { AlarmRule as DeviceProfileAlarmRule }; + export function alarmRuleValidator(control: AbstractControl): ValidationErrors | null { const alarmRule: AlarmRule = control.value; return alarmRuleValid(alarmRule) ? null : {alarmRule: true}; diff --git a/ui-ngx/src/app/shared/models/entity-type.models.ts b/ui-ngx/src/app/shared/models/entity-type.models.ts index 48d2671ada..4a393d9d86 100644 --- a/ui-ngx/src/app/shared/models/entity-type.models.ts +++ b/ui-ngx/src/app/shared/models/entity-type.models.ts @@ -28,6 +28,7 @@ export enum EntityType { DEVICE_PROFILE = 'DEVICE_PROFILE', ASSET_PROFILE = 'ASSET_PROFILE', ALARM = 'ALARM', + ALARM_RULE = 'ALARM_RULE', RULE_CHAIN = 'RULE_CHAIN', RULE_NODE = 'RULE_NODE', EDGE = 'EDGE', @@ -251,12 +252,25 @@ export const entityTypeTranslations = new Map([ [EntityType.CUSTOMER, '/customers'], [EntityType.USER, '/users'], [EntityType.DASHBOARD, '/dashboards'], - [EntityType.ASSET, '/assets'], - [EntityType.DEVICE, '/devices'], + [EntityType.ASSET, '/entities/assets'], + [EntityType.DEVICE, '/entities/devices'], [EntityType.DEVICE_PROFILE, '/profiles/deviceProfiles'], [EntityType.ASSET_PROFILE, '/profiles/assetProfiles'], - [EntityType.RULE_CHAIN, '/ruleChains'], - [EntityType.EDGE, '/edgeInstances'], - [EntityType.ENTITY_VIEW, '/entityViews'], - [EntityType.TB_RESOURCE, '/settings/resources-library'], - [EntityType.OTA_PACKAGE, '/otaUpdates'], + [EntityType.RULE_CHAIN, '/features/ruleChains'], + [EntityType.EDGE, '/edgeManagement/instances'], + [EntityType.ENTITY_VIEW, '/entities/entityViews'], + [EntityType.TB_RESOURCE, '/resources/resources-library'], + [EntityType.OTA_PACKAGE, '/features/otaUpdates'], [EntityType.QUEUE, '/settings/queues'] ]); diff --git a/ui-ngx/src/app/shared/models/id/alarm-rule-id.ts b/ui-ngx/src/app/shared/models/id/alarm-rule-id.ts new file mode 100644 index 0000000000..1026169b09 --- /dev/null +++ b/ui-ngx/src/app/shared/models/id/alarm-rule-id.ts @@ -0,0 +1,26 @@ +/// +/// Copyright © 2016-2023 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 { EntityId } from './entity-id'; +import { EntityType } from '@shared/models/entity-type.models'; + +export class AlarmRuleId implements EntityId { + entityType = EntityType.ALARM_RULE; + id: string; + constructor(id: string) { + this.id = id; + } +} diff --git a/ui-ngx/src/app/shared/models/id/public-api.ts b/ui-ngx/src/app/shared/models/id/public-api.ts index 73bb920200..4eae1b5c27 100644 --- a/ui-ngx/src/app/shared/models/id/public-api.ts +++ b/ui-ngx/src/app/shared/models/id/public-api.ts @@ -15,6 +15,7 @@ /// export * from './alarm-id'; +export * from './alarm-rule-id'; export * from './asset-id'; export * from './audit-log-id'; export * from './customer-id'; diff --git a/ui-ngx/src/app/shared/models/public-api.ts b/ui-ngx/src/app/shared/models/public-api.ts index e2074a5768..07d83158f8 100644 --- a/ui-ngx/src/app/shared/models/public-api.ts +++ b/ui-ngx/src/app/shared/models/public-api.ts @@ -19,6 +19,7 @@ export * from './page/public-api'; export * from './telemetry/telemetry.models'; export * from './time/time.models'; export * from './alarm.models'; +export * from './alarm-rule.models'; export * from './alias.models'; export * from './asset.models'; export * from './audit-log.models'; diff --git a/ui-ngx/src/assets/locale/locale.constant-en_US.json b/ui-ngx/src/assets/locale/locale.constant-en_US.json index 03652d046b..55a6a15dc7 100644 --- a/ui-ngx/src/assets/locale/locale.constant-en_US.json +++ b/ui-ngx/src/assets/locale/locale.constant-en_US.json @@ -76,9 +76,11 @@ "none": "None" }, "admin": { + "settings": "Settings", "general": "General", "general-settings": "General Settings", "home-settings": "Home Settings", + "home": "Home", "outgoing-mail": "Mail Server", "outgoing-mail-settings": "Outgoing Mail Server Settings", "system-settings": "System Settings", @@ -325,6 +327,7 @@ "queue-processing-strategy": "Processing strategy", "queue-configuration": "Queue configuration", "repository-settings": "Repository settings", + "repository": "Repository", "repository-url": "Repository URL", "repository-url-required": "Repository URL is required.", "default-branch": "Default branch name", @@ -346,6 +349,7 @@ "delete-repository-settings-title": "Are you sure you want to delete repository settings?", "delete-repository-settings-text": "Be careful, after the confirmation the repository settings will be removed and version control feature will be unavailable.", "auto-commit-settings": "Auto-commit settings", + "auto-commit": "Auto-commit", "auto-commit-entities": "Auto-commit entities", "no-auto-commit-entities-prompt": "No entities configured for auto-commit", "delete-auto-commit-settings-title": "Are you sure you want to delete auto-commit settings?", @@ -412,6 +416,7 @@ "alarm": { "alarm": "Alarm", "alarms": "Alarms", + "all-alarms": "All alarms", "select-alarm": "Select alarm", "no-alarms-matching": "No alarms matching '{{entity}}' were found.", "alarm-required": "Alarm is required", @@ -479,6 +484,23 @@ "any-type": "Any type", "search-propagated-alarms": "Search propagated alarms" }, + "alarm-rule": { + "rules": "Rules", + "alarm-rule": "Alarm rule", + "alarm-rules": "Alarm rules", + "name": "Name", + "name-required": "Name is required.", + "name-max-length": "Name should be less than 256", + "delete": "Delete", + "alarm-rule-details": "Alarm rule details", + "no-alarm-rules-prompt": "No alarm rules found", + "search": "Search alarm rules", + "selected-alarm-rules": "{ count, plural, 1 {1 alarm rule} other {# alarm rules} } selected", + "delete-alarm-rule-title": "Are you sure you want to delete the alarm rule '{{alarmRuleName}}'?", + "delete-alarm-rule-text": "Be careful, after the confirmation the alarm rule and all related data will become unrecoverable.", + "delete-alarm-rules-title": "Are you sure you want to delete { count, plural, 1 {1 alarm rule} other {# alarm rules} }?", + "delete-alarm-rules-text": "Be careful, after the confirmation all selected alarm rules will be removed and all related data will become unrecoverable." + }, "alias": { "add": "Add alias", "edit": "Edit alias", @@ -1706,6 +1728,7 @@ "edge": { "edge": "Edge", "edge-instances": "Edge instances", + "instances": "Instances", "edge-file": "Edge file", "name-max-length": "Name should be less than 256", "label-max-length": "Label should be less than 256", @@ -1929,8 +1952,12 @@ "dashboard-name-starts-with": "Dashboards whose names start with '{{prefix}}'", "type-alarm": "Alarm", "type-alarms": "Alarms", - "list-of-alarms": "{ count, plural, 1 {One alarms} other {List of # alarms} }", + "list-of-alarms": "{ count, plural, 1 {One alarm} other {List of # alarms} }", "alarm-name-starts-with": "Alarms whose names start with '{{prefix}}'", + "type-alarm-rule": "Alarm rule", + "type-alarm-rules": "Alarm rules", + "list-of-alarm-rules": "{ count, plural, 1 {One alarm rule} other {List of # alarm rules} }", + "alarm-rule-name-starts-with": "Alarm rules whose names start with '{{prefix}}'", "type-rulechain": "Rule chain", "type-rulechains": "Rule chains", "list-of-rulechains": "{ count, plural, 1 {One rule chain} other {List of # rule chains} }", @@ -2295,6 +2322,9 @@ "file": "Extensions file", "invalid-file-error": "Invalid extension file" }, + "feature": { + "advanced-features": "Advanced features" + }, "filter": { "add": "Add filter", "edit": "Edit filter", @@ -2676,6 +2706,14 @@ "copy-code": "Click to copy", "copied": "Copied!" }, + "notification": { + "notification-center": "Notification center", + "inbox": "Inbox", + "sent": "Sent", + "templates": "Templates", + "recipients": "Recipients", + "rules": "Rules" + }, "ota-update": { "add": "Add package", "assign-firmware": "Assigned firmware",