Merge pull request #8911 from vvlladd28/feature/change-user-profile

Redesign user menu
This commit is contained in:
Igor Kulikov 2023-07-31 16:29:14 +03:00 committed by GitHub
commit 3548dd4e1d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
11 changed files with 165 additions and 30 deletions

View File

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

View File

@ -20,6 +20,7 @@
<nav fxFlex *ngIf="!routerOutlet?.activatedRouteData?.hideTabs && !hideCurrentTabs && (tabs$ | async).length > 1" mat-tab-nav-bar mat-stretch-tabs="false" class="tb-router-tabs" [tabPanel]="tabPanel">
<a *ngFor="let tab of tabs$ | async"
routerLink="{{tab.path}}"
replaceUrl="{{ replaceUrl }}"
routerLinkActive
#rla="routerLinkActive"
mat-tab-link

View File

@ -20,8 +20,8 @@ import { Store } from '@ngrx/store';
import { AppState } from '@core/core.state';
import { ActivatedRoute, NavigationEnd, Router } from '@angular/router';
import { MenuService } from '@core/services/menu.service';
import { distinctUntilChanged, filter, map, mergeMap, take } from 'rxjs/operators';
import { merge } from 'rxjs';
import { distinctUntilChanged, filter, map, mergeMap, startWith, take } from 'rxjs/operators';
import { merge, Observable } from 'rxjs';
import { MenuSection } from '@core/services/menu.models';
import { ActiveComponentService } from '@core/services/active-component.service';
import { TbAnchorComponent } from '@shared/components/tb-anchor.component';
@ -39,14 +39,9 @@ export class RouterTabsComponent extends PageComponent implements OnInit {
hideCurrentTabs = false;
tabs$ = merge(this.menuService.menuSections(),
this.router.events.pipe(
filter((event) => event instanceof NavigationEnd ),
distinctUntilChanged())
).pipe(
mergeMap(() => this.menuService.menuSections().pipe(take(1))),
map((sections) => this.buildTabs(this.activatedRoute, sections))
);
replaceUrl = false;
tabs$: Observable<Array<MenuSection>>;
constructor(protected store: Store<AppState>,
private activatedRoute: ActivatedRoute,
@ -57,6 +52,27 @@ export class RouterTabsComponent extends PageComponent implements OnInit {
}
ngOnInit() {
if (this.activatedRoute.snapshot.data.useChildrenRoutesForTabs) {
this.tabs$ = this.router.events.pipe(
filter((event) => event instanceof NavigationEnd),
startWith(''),
map(() => this.buildTabsForRoutes(this.activatedRoute))
);
} else {
this.tabs$ = merge(this.menuService.menuSections(),
this.router.events.pipe(
filter((event) => event instanceof NavigationEnd ),
distinctUntilChanged())
).pipe(
mergeMap(() => this.menuService.menuSections().pipe(take(1))),
map((sections) => this.buildTabs(this.activatedRoute, sections))
);
}
if (this.activatedRoute.snapshot.data.replaceUrl) {
this.replaceUrl = true;
}
this.activatedRoute.data.subscribe(
(data) => this.buildTabsHeaderComponent(data)
);
@ -80,18 +96,36 @@ export class RouterTabsComponent extends PageComponent implements OnInit {
}
}
private buildTabs(activatedRoute: ActivatedRoute, sections: MenuSection[]): Array<MenuSection> {
const sectionPath = '/' + activatedRoute.pathFromRoot.map(r => r.snapshot.url)
private getSectionPath(activatedRoute: ActivatedRoute): string {
return '/' + activatedRoute.pathFromRoot.map(r => r.snapshot.url)
.filter(f => !!f[0]).map(f => f.map(f1 => f1.path).join('/')).join('/');
}
private buildTabs(activatedRoute: ActivatedRoute, sections: MenuSection[]): Array<MenuSection> {
const sectionPath = this.getSectionPath(activatedRoute);
const found = this.findRootSection(sections, sectionPath);
if (found) {
const rootPath = sectionPath.substring(0, sectionPath.length - found.path.length);
const isRoot = rootPath === '';
const tabs: Array<MenuSection> = found ? found.pages.filter(page => !page.disabled && (!page.rootOnly || isRoot)) : [];
return tabs.map((tab) => ({...tab, path: rootPath + tab.path}));
} else {
return [];
}
return [];
}
private buildTabsForRoutes(activatedRoute: ActivatedRoute): Array<MenuSection> {
const sectionPath = this.getSectionPath(activatedRoute);
if (activatedRoute.routeConfig.children.length) {
const activeRouterChildren = activatedRoute.routeConfig.children.filter(page => page.path !== '');
return activeRouterChildren.map(tab => ({
id: tab.component.name,
type: 'link',
name: tab.data?.breadcrumb?.label ?? '',
icon: tab.data?.breadcrumb?.icon ?? '',
path: `${sectionPath}/${tab.path}`
}));
}
return [];
}
private findRootSection(sections: MenuSection[], sectionPath: string): MenuSection {

View File

@ -0,0 +1,61 @@
///
/// 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 { inject, 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';
import { getCurrentAuthState } from '@core/auth/auth.selectors';
import { Store } from '@ngrx/store';
import { AppState } from '@core/core.state';
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'
},
useChildrenRoutesForTabs: true,
},
resolve: {
replaceUrl: () => getCurrentAuthState(inject(Store<AppState>)).forceFullscreen
},
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 { FeaturesModule } from '@home/pages/features/features.module';
import { NotificationModule } from '@home/pages/notification/notification.module';
import { AccountModule } from '@home/pages/account/account.module';
@NgModule({
exports: [
@ -70,7 +71,8 @@ import { NotificationModule } from '@home/pages/notification/notification.module
ApiUsageModule,
OtaUpdateModule,
UserModule,
VcModule
VcModule,
AccountModule
]
})
export class HomePagesModule { }

View File

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

View File

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

View File

@ -28,13 +28,9 @@
</button>
<mat-menu #userMenu="matMenu" xPosition="before">
<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>
<span translate>home.profile</span>
</button>
<button mat-menu-item (click)="openSecurity()">
<mat-icon class="material-icons">lock</mat-icon>
<span translate>security.security</span>
<span translate>account.account</span>
</button>
<button mat-menu-item (click)="logout()">
<mat-icon class="material-icons">exit_to_app</mat-icon>

View File

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

View File

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