UI: Home links refactoring.

This commit is contained in:
Igor Kulikov 2024-08-20 14:46:28 +03:00
parent 4a5dabdad8
commit 72134abbf9
5 changed files with 126 additions and 330 deletions

View File

@ -38,15 +38,14 @@ export interface MenuReference {
pages?: Array<MenuReference>;
}
export interface HomeSection {
export interface HomeSectionReference {
name: string;
places: Array<HomeSectionPlace>;
places: Array<MenuId>;
}
export interface HomeSectionPlace {
export interface HomeSection {
name: string;
icon: string;
path: string;
places: Array<MenuSection>;
}
export enum MenuId {
@ -768,11 +767,108 @@ const defaultUserMenuMap = new Map<Authority, MenuReference[]>([
]
]);
const defaultHomeSectionMap = new Map<Authority, HomeSectionReference[]>([
[
Authority.SYS_ADMIN,
[
{
name: 'tenant.management',
places: [MenuId.tenants, MenuId.tenant_profiles]
},
{
name: 'widget.management',
places: [MenuId.widget_library]
},
{
name: 'admin.system-settings',
places: [MenuId.general, MenuId.mail_server,
MenuId.notification_settings, MenuId.security_settings, MenuId.oauth2, MenuId.two_fa, MenuId.resources_library, MenuId.queues]
}
]
],
[
Authority.TENANT_ADMIN,
[
{
name: 'rulechain.management',
places: [MenuId.rule_chains]
},
{
name: 'customer.management',
places: [MenuId.customers]
},
{
name: 'asset.management',
places: [MenuId.assets, MenuId.asset_profiles]
},
{
name: 'device.management',
places: [MenuId.devices, MenuId.device_profiles, MenuId.otaUpdates]
},
{
name: 'entity-view.management',
places: [MenuId.entity_views]
},
{
name: 'edge.management',
places: [MenuId.edges, MenuId.rulechain_templates]
},
{
name: 'dashboard.management',
places: [MenuId.widget_library, MenuId.dashboards]
},
{
name: 'version-control.management',
places: [MenuId.version_control]
},
{
name: 'audit-log.audit',
places: [MenuId.audit_log, MenuId.api_usage]
},
{
name: 'admin.system-settings',
places: [MenuId.home_settings, MenuId.resources_library, MenuId.repository_settings, MenuId.auto_commit_settings]
}
]
],
[
Authority.CUSTOMER_USER,
[
{
name: 'asset.view-assets',
places: [MenuId.assets]
},
{
name: 'device.view-devices',
places: [MenuId.devices]
},
{
name: 'entity-view.management',
places: [MenuId.entity_views]
},
{
name: 'edge.management',
places: [MenuId.edges]
},
{
name: 'dashboard.view-dashboards',
places: [MenuId.dashboards]
}
]
]
]);
export const buildUserMenu = (authState: AuthState): Array<MenuSection> => {
const references = defaultUserMenuMap.get(authState.authUser.authority);
return (references || []).map(ref => referenceToMenuSection(authState, ref)).filter(section => !!section);
};
export const buildUserHome = (authState: AuthState, availableMenuSections: MenuSection[]): Array<HomeSection> => {
const references = defaultHomeSectionMap.get(authState.authUser.authority);
return (references || []).map(ref =>
homeReferenceToHomeSection(availableMenuSections, ref)).filter(section => !!section);
};
const referenceToMenuSection = (authState: AuthState, reference: MenuReference): MenuSection | undefined => {
if (filterMenuReference(authState, reference)) {
const section = menuSectionMap.get(reference.id);
@ -807,3 +903,15 @@ const filterMenuReference = (authState: AuthState, reference: MenuReference): bo
return true;
}
};
const homeReferenceToHomeSection = (availableMenuSections: MenuSection[], reference: HomeSectionReference): HomeSection | undefined => {
const places = reference.places.map(id => availableMenuSections.find(m => m.id === id)).filter(p => !!p);
if (places.length) {
return {
name: reference.name,
places
};
} else {
return undefined;
}
};

View File

@ -19,9 +19,8 @@ import { select, Store } from '@ngrx/store';
import { AppState } from '../core.state';
import { getCurrentOpenedMenuSections, selectAuth, selectIsAuthenticated } from '../auth/auth.selectors';
import { filter, map, take } from 'rxjs/operators';
import { buildUserMenu, HomeSection, MenuId, MenuSection } from '@core/services/menu.models';
import { BehaviorSubject, ReplaySubject, Observable, Subject } from 'rxjs';
import { Authority } from '@shared/models/authority.enum';
import { buildUserHome, buildUserMenu, HomeSection, MenuId, MenuSection } from '@core/services/menu.models';
import { Observable, ReplaySubject, Subject } from 'rxjs';
import { AuthState } from '@core/auth/auth.models';
import { NavigationEnd, Router } from '@angular/router';
@ -32,13 +31,11 @@ export class MenuService {
private currentMenuSections: Array<MenuSection>;
private menuSections$: Subject<Array<MenuSection>> = new ReplaySubject<Array<MenuSection>>(1);
private homeSections$: Subject<Array<HomeSection>> = new BehaviorSubject<Array<HomeSection>>([]);
private homeSections$: Subject<Array<HomeSection>> = new ReplaySubject<Array<HomeSection>>(1);
private availableMenuSections$: Subject<Array<MenuSection>> = new ReplaySubject<Array<MenuSection>>(1);
private availableMenuLinks$ = this.menuSections$.pipe(
map((items) => this.allMenuLinks(items))
);
private availableMenuSections$ = this.menuSections$.pipe(
map((items) => this.allMenuSections(items))
);
constructor(private store: Store<AppState>,
private router: Router) {
@ -60,21 +57,12 @@ export class MenuService {
this.store.pipe(select(selectAuth), take(1)).subscribe(
(authState: AuthState) => {
if (authState.authUser) {
let homeSections: Array<HomeSection>;
switch (authState.authUser.authority) {
case Authority.SYS_ADMIN:
homeSections = this.buildSysAdminHome();
break;
case Authority.TENANT_ADMIN:
homeSections = this.buildTenantAdminHome(authState);
break;
case Authority.CUSTOMER_USER:
homeSections = this.buildCustomerUserHome(authState);
break;
}
this.currentMenuSections = buildUserMenu(authState);
this.updateOpenedMenuSections();
this.menuSections$.next(this.currentMenuSections);
const availableMenuSections = this.allMenuSections(this.currentMenuSections);
this.availableMenuSections$.next(availableMenuSections);
const homeSections = buildUserHome(authState, availableMenuSections);
this.homeSections$.next(homeSections);
}
}
@ -90,304 +78,6 @@ export class MenuService {
);
}
private buildSysAdminHome(): Array<HomeSection> {
const homeSections: Array<HomeSection> = [];
homeSections.push(
{
name: 'tenant.management',
places: [
{
name: 'tenant.tenants',
icon: 'supervisor_account',
path: '/tenants'
},
{
name: 'tenant-profile.tenant-profiles',
icon: 'mdi:alpha-t-box',
path: '/tenantProfiles'
},
]
},
{
name: 'widget.management',
places: [
{
name: 'widget.widget-library',
icon: 'now_widgets',
path: '/resources/widgets-library',
}
]
},
{
name: 'admin.system-settings',
places: [
{
name: 'admin.general',
icon: 'settings_applications',
path: '/settings/general'
},
{
name: 'admin.outgoing-mail',
icon: 'mail',
path: '/settings/outgoing-mail'
},
{
name: 'admin.sms-provider',
icon: 'sms',
path: '/settings/sms-provider'
},
{
name: 'admin.security-settings',
icon: 'security',
path: '/settings/security-settings'
},
{
name: 'admin.oauth2.oauth2',
icon: 'security',
path: '/settings/oauth2'
},
{
name: 'admin.2fa.2fa',
icon: 'mdi:two-factor-authentication',
path: '/settings/2fa'
},
{
name: 'resource.resources-library',
icon: 'folder',
path: '/settings/resources-library'
},
{
name: 'admin.queues',
icon: 'swap_calls',
path: '/settings/queues'
},
]
}
);
return homeSections;
}
private buildTenantAdminHome(authState: AuthState): Array<HomeSection> {
const homeSections: Array<HomeSection> = [];
homeSections.push(
{
name: 'rulechain.management',
places: [
{
name: 'rulechain.rulechains',
icon: 'settings_ethernet',
path: '/ruleChains'
}
]
},
{
name: 'customer.management',
places: [
{
name: 'customer.customers',
icon: 'supervisor_account',
path: '/customers'
}
]
},
{
name: 'asset.management',
places: [
{
name: 'asset.assets',
icon: 'domain',
path: '/assets'
},
{
name: 'asset-profile.asset-profiles',
icon: 'mdi:alpha-a-box',
path: '/profiles/assetProfiles'
}
]
},
{
name: 'device.management',
places: [
{
name: 'device.devices',
icon: 'devices_other',
path: '/devices'
},
{
name: 'device-profile.device-profiles',
icon: 'mdi:alpha-d-box',
path: '/profiles/deviceProfiles'
},
{
name: 'ota-update.ota-updates',
icon: 'memory',
path: '/otaUpdates'
}
]
},
{
name: 'entity-view.management',
places: [
{
name: 'entity-view.entity-views',
icon: 'view_quilt',
path: '/entityViews'
}
]
}
);
if (authState.edgesSupportEnabled) {
homeSections.push(
{
name: 'edge.management',
places: [
{
name: 'edge.edge-instances',
icon: 'router',
path: '/edgeInstances'
},
{
name: 'edge.rulechain-templates',
icon: 'settings_ethernet',
path: '/edgeManagement/ruleChains'
}
]
}
);
}
homeSections.push(
{
name: 'dashboard.management',
places: [
{
name: 'widget.widget-library',
icon: 'now_widgets',
path: '/widgets-bundles'
},
{
name: 'dashboard.dashboards',
icon: 'dashboard',
path: '/dashboards'
}
]
},
{
name: 'version-control.management',
places: [
{
name: 'version-control.version-control',
icon: 'history',
path: '/vc'
}
]
},
{
name: 'audit-log.audit',
places: [
{
name: 'audit-log.audit-logs',
icon: 'track_changes',
path: '/auditLogs'
},
{
name: 'api-usage.api-usage',
icon: 'insert_chart',
path: '/usage'
}
]
},
{
name: 'admin.system-settings',
places: [
{
name: 'admin.home-settings',
icon: 'settings_applications',
path: '/settings/home'
},
{
name: 'resource.resources-library',
icon: 'folder',
path: '/settings/resources-library'
},
{
name: 'admin.repository-settings',
icon: 'manage_history',
path: '/settings/repository',
},
{
name: 'admin.auto-commit-settings',
icon: 'settings_backup_restore',
path: '/settings/auto-commit'
}
]
}
);
return homeSections;
}
private buildCustomerUserHome(authState: AuthState): Array<HomeSection> {
const homeSections: Array<HomeSection> = [];
homeSections.push(
{
name: 'asset.view-assets',
places: [
{
name: 'asset.assets',
icon: 'domain',
path: '/assets'
}
]
},
{
name: 'device.view-devices',
places: [
{
name: 'device.devices',
icon: 'devices_other',
path: '/devices'
}
]
},
{
name: 'entity-view.management',
places: [
{
name: 'entity-view.entity-views',
icon: 'view_quilt',
path: '/entityViews'
}
]
}
);
if (authState.edgesSupportEnabled) {
homeSections.push(
{
name: 'edge.management',
places: [
{
name: 'edge.edge-instances',
icon: 'settings_input_antenna',
path: '/edgeInstances'
}
]
}
);
}
homeSections.push(
{
name: 'dashboard.view-dashboards',
places: [
{
name: 'dashboard.dashboards',
icon: 'dashboard',
path: '/dashboards'
}
]
}
);
return homeSections;
}
private allMenuLinks(sections: Array<MenuSection>): Array<MenuSection> {
const result: Array<MenuSection> = [];
for (const section of sections) {

View File

@ -26,7 +26,7 @@
<mat-grid-tile *ngFor="let place of sectionPlaces(section)">
<a mat-raised-button color="primary" class="tb-card-button" href="{{place.path}}" (click)="navigate($event, place.path)">
<tb-icon matButtonIcon class="tb-mat-96">{{place.icon}}</tb-icon>
<span translate>{{place.name}}</span>
<span>{{place.customTranslate ? (place.name | customTranslate) : (place.name | translate)}}</span>
</a>
</mat-grid-tile>
</mat-grid-list>

View File

@ -20,7 +20,7 @@ import { WidgetContext } from '@home/models/widget-component.models';
import { Store } from '@ngrx/store';
import { AppState } from '@core/core.state';
import { MenuService } from '@core/services/menu.service';
import { HomeSection, HomeSectionPlace } from '@core/services/menu.models';
import { HomeSection, MenuSection } from '@core/services/menu.models';
import { Router } from '@angular/router';
import { map } from 'rxjs/operators';
@ -38,9 +38,7 @@ export class NavigationCardsWidgetComponent extends PageComponent implements OnI
homeSections$ = this.menuService.homeSections();
showHomeSections$ = this.homeSections$.pipe(
map((sections) => {
return sections.filter((section) => this.sectionPlaces(section).length > 0);
})
map((sections) => sections.filter((section) => this.sectionPlaces(section).length > 0))
);
cols = null;
@ -85,11 +83,11 @@ export class NavigationCardsWidgetComponent extends PageComponent implements OnI
});
}
sectionPlaces(section: HomeSection): HomeSectionPlace[] {
sectionPlaces(section: HomeSection): MenuSection[] {
return section && section.places ? section.places.filter((place) => this.filterPlace(place)) : [];
}
private filterPlace(place: HomeSectionPlace): boolean {
private filterPlace(place: MenuSection): boolean {
if (this.settings.filterType === 'include') {
return this.settings.filter.includes(place.path);
} else if (this.settings.filterType === 'exclude') {

View File

@ -28,7 +28,7 @@
<mat-grid-tile *ngFor="let place of section.places">
<a mat-raised-button color="primary" class="tb-card-button" routerLink="{{place.path}}">
<tb-icon matButtonIcon class="tb-mat-96">{{place.icon}}</tb-icon>
<span translate>{{place.name}}</span>
<span>{{place.customTranslate ? (place.name | customTranslate) : (place.name | translate)}}</span>
</a>
</mat-grid-tile>
</mat-grid-list>