UI: Redesign user menu: move profile and security menu item to account item

This commit is contained in:
Vladyslav_Prykhodko 2023-07-11 10:40:04 +03:00
parent 738cb5cebf
commit f7b60e1c0e
12 changed files with 210 additions and 21 deletions

View File

@ -244,7 +244,7 @@ export class AuthService {
if (authState && authState.authUser) { if (authState && authState.authUser) {
if (authState.authUser.authority === Authority.TENANT_ADMIN || authState.authUser.authority === Authority.CUSTOMER_USER) { if (authState.authUser.authority === Authority.TENANT_ADMIN || authState.authUser.authority === Authority.CUSTOMER_USER) {
if ((this.userHasDefaultDashboard(authState) && authState.forceFullscreen) || authState.authUser.isPublic) { if ((this.userHasDefaultDashboard(authState) && authState.forceFullscreen) || authState.authUser.isPublic) {
if (path === 'profile' || path === 'security') { if (path.startsWith('account')) {
if (this.userHasProfile(authState.authUser)) { if (this.userHasProfile(authState.authUser)) {
return false; return false;
} else { } else {

View File

@ -262,6 +262,34 @@ export class MenuService {
isMdiIcon: true isMdiIcon: true
} }
] ]
},
{
id: 'account',
name: 'profile.profile',
type: 'link',
path: '/account',
disabled: true,
icon: 'mdi:message-badge',
isMdiIcon: true,
pages: [
{
id: 'personal_info',
name: 'account.personal-info',
fullName: 'account.personal-info',
type: 'link',
path: '/account/profile',
icon: 'mdi:badge-account-horizontal',
isMdiIcon: true
},
{
id: 'security',
name: 'security.security',
fullName: 'security.security',
type: 'link',
path: '/account/security',
icon: 'lock'
}
]
} }
); );
return sections; return sections;
@ -634,6 +662,34 @@ export class MenuService {
icon: 'track_changes' icon: 'track_changes'
} }
] ]
},
{
id: 'account',
name: 'profile.profile',
type: 'link',
path: '/account',
disabled: true,
icon: 'mdi:message-badge',
isMdiIcon: true,
pages: [
{
id: 'personal_info',
name: 'account.personal-info',
fullName: 'account.personal-info',
type: 'link',
path: '/account/profile',
icon: 'mdi:badge-account-horizontal',
isMdiIcon: true
},
{
id: 'security',
name: 'security.security',
fullName: 'security.security',
type: 'link',
path: '/account/security',
icon: 'lock'
}
]
} }
); );
return sections; return sections;
@ -885,6 +941,34 @@ export class MenuService {
icon: 'inbox' icon: 'inbox'
} }
] ]
},
{
id: 'account',
name: 'profile.profile',
type: 'link',
path: '/account',
disabled: true,
icon: 'mdi:message-badge',
isMdiIcon: true,
pages: [
{
id: 'personal_info',
name: 'account.personal-info',
fullName: 'account.personal-info',
type: 'link',
path: '/account/profile',
icon: 'mdi:badge-account-horizontal',
isMdiIcon: true
},
{
id: 'security',
name: 'security.security',
fullName: 'security.security',
type: 'link',
path: '/account/security',
icon: 'lock'
}
]
} }
); );
return sections; return sections;

View File

@ -14,7 +14,7 @@
/// limitations under the License. /// limitations under the License.
/// ///
import { AfterViewInit, Component, ElementRef, Inject, OnInit, ViewChild } from '@angular/core'; import { AfterViewInit, Component, ElementRef, OnInit, ViewChild } from '@angular/core';
import { fromEvent } from 'rxjs'; import { fromEvent } from 'rxjs';
import { Store } from '@ngrx/store'; import { Store } from '@ngrx/store';
import { debounceTime, distinctUntilChanged, tap } from 'rxjs/operators'; import { debounceTime, distinctUntilChanged, tap } from 'rxjs/operators';
@ -27,10 +27,10 @@ import { MediaBreakpoints } from '@shared/models/constants';
import screenfull from 'screenfull'; import screenfull from 'screenfull';
import { MatSidenav } from '@angular/material/sidenav'; import { MatSidenav } from '@angular/material/sidenav';
import { AuthState } from '@core/auth/auth.models'; import { AuthState } from '@core/auth/auth.models';
import { WINDOW } from '@core/services/window.service';
import { instanceOfSearchableComponent, ISearchableComponent } from '@home/models/searchable-component.models'; import { instanceOfSearchableComponent, ISearchableComponent } from '@home/models/searchable-component.models';
import { ActiveComponentService } from '@core/services/active-component.service'; import { ActiveComponentService } from '@core/services/active-component.service';
import { RouterTabsComponent } from '@home/components/router-tabs.component'; import { RouterTabsComponent } from '@home/components/router-tabs.component';
import { Router } from '@angular/router';
@Component({ @Component({
selector: 'tb-home', selector: 'tb-home',
@ -65,8 +65,8 @@ export class HomeComponent extends PageComponent implements AfterViewInit, OnIni
hideLoadingBar = false; hideLoadingBar = false;
constructor(protected store: Store<AppState>, constructor(protected store: Store<AppState>,
@Inject(WINDOW) private window: Window,
private activeComponentService: ActiveComponentService, private activeComponentService: ActiveComponentService,
private router: Router,
public breakpointObserver: BreakpointObserver) { public breakpointObserver: BreakpointObserver) {
super(store); super(store);
} }
@ -120,7 +120,8 @@ export class HomeComponent extends PageComponent implements AfterViewInit, OnIni
} }
goBack() { goBack() {
this.window.history.back(); const dashboardId = this.authState.userDetails.additionalInfo.defaultDashboardId;
this.router.navigate(['dashboard', dashboardId]).then(() => {});
} }
activeComponentChanged(activeComponent: any) { activeComponentChanged(activeComponent: any) {

View File

@ -17,6 +17,8 @@
import { ChangeDetectionStrategy, Component, OnInit } from '@angular/core'; import { ChangeDetectionStrategy, Component, OnInit } from '@angular/core';
import { MenuService } from '@core/services/menu.service'; import { MenuService } from '@core/services/menu.service';
import { MenuSection } from '@core/services/menu.models'; import { MenuSection } from '@core/services/menu.models';
import { Observable, of } from 'rxjs';
import { mergeMap, share } from 'rxjs/operators';
@Component({ @Component({
selector: 'tb-side-menu', selector: 'tb-side-menu',
@ -26,15 +28,23 @@ import { MenuSection } from '@core/services/menu.models';
}) })
export class SideMenuComponent implements OnInit { export class SideMenuComponent implements OnInit {
menuSections$ = this.menuService.menuSections(); menuSections$: Observable<Array<MenuSection>>;
constructor(private menuService: MenuService) { constructor(private menuService: MenuService) {
this.menuSections$ = this.menuService.menuSections().pipe(
mergeMap((sections) => this.filterSections(sections)),
share()
);
} }
trackByMenuSection(index: number, section: MenuSection){ trackByMenuSection(index: number, section: MenuSection){
return section.id; return section.id;
} }
private filterSections(sections: Array<MenuSection>): Observable<Array<MenuSection>> {
return of(sections.filter(section => !section.disabled));
}
ngOnInit() { ngOnInit() {
} }

View File

@ -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 { NgModule } from '@angular/core';
import { RouterModule, Routes } from '@angular/router';
import { RouterTabsComponent } from '@home/components/router-tabs.component';
import { Authority } from '@shared/models/authority.enum';
import { securityRoutes } from '@home/pages/security/security-routing.module';
import { profileRoutes } from '@home/pages/profile/profile-routing.module';
const routes: Routes = [
{
path: 'account',
component: RouterTabsComponent,
data: {
auth: [Authority.SYS_ADMIN, Authority.TENANT_ADMIN, Authority.CUSTOMER_USER],
breadcrumb: {
label: 'account.account',
icon: 'account_circle'
}
},
children: [
{
path: '',
children: [],
data: {
auth: [Authority.SYS_ADMIN, Authority.TENANT_ADMIN, Authority.CUSTOMER_USER],
redirectTo: '/account/profile',
}
},
...profileRoutes,
...securityRoutes
]
}
];
@NgModule({
imports: [RouterModule.forChild(routes)],
exports: [RouterModule]
})
export class AccountRoutingModule { }

View File

@ -0,0 +1,28 @@
///
/// 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 { AccountRoutingModule } from '@home/pages/account/account-routing.module';
import { CommonModule } from '@angular/common';
@NgModule({
declarations: [ ],
imports: [
CommonModule,
AccountRoutingModule
]
})
export class AccountModule { }

View File

@ -42,6 +42,7 @@ import { AlarmModule } from '@home/pages/alarm/alarm.module';
import { EntitiesModule } from '@home/pages/entities/entities.module'; import { EntitiesModule } from '@home/pages/entities/entities.module';
import { FeaturesModule } from '@home/pages/features/features.module'; import { FeaturesModule } from '@home/pages/features/features.module';
import { NotificationModule } from '@home/pages/notification/notification.module'; import { NotificationModule } from '@home/pages/notification/notification.module';
import { AccountModule } from '@home/pages/account/account.module';
@NgModule({ @NgModule({
exports: [ exports: [
@ -70,7 +71,8 @@ import { NotificationModule } from '@home/pages/notification/notification.module
ApiUsageModule, ApiUsageModule,
OtaUpdateModule, OtaUpdateModule,
UserModule, UserModule,
VcModule VcModule,
AccountModule
] ]
}) })
export class HomePagesModule { } export class HomePagesModule { }

View File

@ -40,7 +40,7 @@ export class UserProfileResolver implements Resolve<User> {
} }
} }
const routes: Routes = [ export const profileRoutes: Routes = [
{ {
path: 'profile', path: 'profile',
component: ProfileComponent, component: ProfileComponent,
@ -59,6 +59,13 @@ const routes: Routes = [
} }
]; ];
const routes: Routes = [
{
path: 'profile',
redirectTo: 'account/profile'
}
];
@NgModule({ @NgModule({
imports: [RouterModule.forChild(routes)], imports: [RouterModule.forChild(routes)],
exports: [RouterModule], exports: [RouterModule],

View File

@ -53,7 +53,7 @@ export class UserTwoFAProvidersResolver implements Resolve<Array<TwoFactorAuthPr
} }
} }
const routes: Routes = [ export const securityRoutes: Routes = [
{ {
path: 'security', path: 'security',
component: SecurityComponent, component: SecurityComponent,
@ -73,6 +73,13 @@ const routes: Routes = [
} }
]; ];
const routes: Routes = [
{
path: 'security',
redirectTo: '/account/security'
}
];
@NgModule({ @NgModule({
imports: [RouterModule.forChild(routes)], imports: [RouterModule.forChild(routes)],
exports: [RouterModule], exports: [RouterModule],

View File

@ -28,13 +28,9 @@
</button> </button>
<mat-menu #userMenu="matMenu" xPosition="before"> <mat-menu #userMenu="matMenu" xPosition="before">
<div class="tb-user-menu-items" *ngIf="authority$ | async; let authority"> <div class="tb-user-menu-items" *ngIf="authority$ | async; let authority">
<button mat-menu-item (click)="openProfile()"> <button mat-menu-item (click)="openAccount()">
<mat-icon class="material-icons">account_circle</mat-icon> <mat-icon class="material-icons">account_circle</mat-icon>
<span translate>home.profile</span> <span translate>account.account</span>
</button>
<button mat-menu-item (click)="openSecurity()">
<mat-icon class="material-icons">lock</mat-icon>
<span translate>security.security</span>
</button> </button>
<button mat-menu-item (click)="logout()"> <button mat-menu-item (click)="logout()">
<mat-icon class="material-icons">exit_to_app</mat-icon> <mat-icon class="material-icons">exit_to_app</mat-icon>

View File

@ -102,12 +102,8 @@ export class UserMenuComponent implements OnInit, OnDestroy {
return name; return name;
} }
openProfile(): void { openAccount(): void {
this.router.navigate(['profile']); this.router.navigate(['account']);
}
openSecurity(): void {
this.router.navigate(['security']);
} }
logout(): void { logout(): void {

View File

@ -10,6 +10,10 @@
"permission-denied": "Permission Denied", "permission-denied": "Permission Denied",
"permission-denied-text": "You don't have permission to perform this operation!" "permission-denied-text": "You don't have permission to perform this operation!"
}, },
"account": {
"account": "Account",
"personal-info": "Personal info"
},
"action": { "action": {
"activate": "Activate", "activate": "Activate",
"suspend": "Suspend", "suspend": "Suspend",