Skip to content

Commit f232109

Browse files
jnizetcexbrayat
authored andcommittedMay 1, 2024
refactor: improve navbar
1 parent 077f114 commit f232109

File tree

4 files changed

+107
-85
lines changed

4 files changed

+107
-85
lines changed
 

‎.gitignore

+3
Original file line numberDiff line numberDiff line change
@@ -35,3 +35,6 @@ secrets
3535
Thumbs.db
3636

3737
examples
38+
39+
.nx
40+
.sdkmanrc
+75-70
Original file line numberDiff line numberDiff line change
@@ -1,74 +1,79 @@
1-
<nav class="navbar navbar-expand-md bg-dark py-0" data-bs-theme="dark">
2-
<div class="container-fluid">
3-
<span class="navbar-brand">
4-
<img src="assets/logo.png" alt="" />
5-
</span>
6-
<button
7-
class="navbar-toggler"
8-
type="button"
9-
[attr.aria-expanded]="!collapsed"
10-
[attr.aria-label]="'navbar.toggle' | translate"
11-
(click)="collapsed = !collapsed"
12-
>
13-
<span class="navbar-toggler-icon"></span>
14-
</button>
15-
<div class="collapse navbar-collapse" [class.show]="!collapsed">
16-
<ul class="navbar-nav me-auto">
17-
@if (hasPermission('ORDER_MANAGEMENT') || hasPermission('ORDER_VISUALIZATION')) {
18-
<li class="nav-item" id="navbar-orders">
19-
<a class="nav-link" routerLink="/orders" (click)="collapsed = true">
20-
<fa-icon [icon]="ordersIcon" class="me-1"></fa-icon>
21-
<span translate="navbar.orders"></span>
22-
</a>
23-
</li>
24-
}
25-
26-
@if (hasPermission('ADMINISTRATION')) {
27-
<li class="nav-item" ngbDropdown>
28-
<a class="nav-link" ngbDropdownToggle id="navbar-administration-dropdown" role="button" tabindex="0">
29-
<fa-icon [icon]="administrationIcon" class="me-1"></fa-icon>
30-
<span translate="navbar.administration"></span>
31-
</a>
32-
<div ngbDropdownMenu aria-labelledby="navbar-administration-dropdown" class="dropdown-menu">
33-
<a ngbDropdownItem routerLink="/grcs" (click)="collapsed = true" id="navbar-grcs">
34-
<fa-icon [icon]="grcIcon" class="me-1"></fa-icon>
35-
<span translate="navbar.grcs"></span>
36-
</a>
37-
<a ngbDropdownItem routerLink="/accession-holders" (click)="collapsed = true" id="navbar-accession-holders">
38-
<fa-icon [icon]="accessionHolderIcon" class="me-1"></fa-icon>
39-
<span translate="navbar.accession-holders"></span>
1+
@if (vm$ | async; as vm) {
2+
<nav class="navbar navbar-expand-md bg-dark py-0" data-bs-theme="dark">
3+
<div class="container-fluid">
4+
<span class="navbar-brand">
5+
<img src="assets/logo.png" alt="" />
6+
</span>
7+
<button
8+
class="navbar-toggler"
9+
type="button"
10+
[attr.aria-expanded]="!collapsed"
11+
[attr.aria-label]="'navbar.toggle' | translate"
12+
(click)="collapsed.set(!collapsed())"
13+
>
14+
<span class="navbar-toggler-icon"></span>
15+
</button>
16+
<div class="collapse navbar-collapse" [ngbCollapse]="collapsed()">
17+
<ul class="navbar-nav me-auto">
18+
@if (hasPermission(vm, 'ORDER_MANAGEMENT') || hasPermission(vm, 'ORDER_VISUALIZATION')) {
19+
<li class="nav-item" id="navbar-orders">
20+
<a class="nav-link" routerLink="/orders" (click)="collapsed.set(true)">
21+
<fa-icon [icon]="ordersIcon" class="me-1"></fa-icon>
22+
<span translate="navbar.orders"></span>
4023
</a>
41-
<a ngbDropdownItem routerLink="/users" (click)="collapsed = true" id="navbar-users">
42-
<fa-icon [icon]="usersIcon" class="me-1"></fa-icon>
43-
<span translate="navbar.users"></span>
24+
</li>
25+
}
26+
27+
@if (hasPermission(vm, 'ADMINISTRATION')) {
28+
<li class="nav-item" ngbDropdown>
29+
<a class="nav-link" ngbDropdownToggle id="navbar-administration-dropdown" role="button" tabindex="0">
30+
<fa-icon [icon]="administrationIcon" class="me-1"></fa-icon>
31+
<span translate="navbar.administration"></span>
4432
</a>
45-
</div>
46-
</li>
47-
}
48-
</ul>
49-
<ul class="navbar-nav align-items-baseline">
50-
@if (user) {
51-
<li class="nav-item" class="me-2" id="navbar-user">
52-
<span class="navbar-text">
53-
<fa-icon [icon]="userIcon" class="me-1"></fa-icon>
54-
{{ user.name }}
55-
</span>
56-
</li>
57-
}
58-
<li>
59-
@if (user) {
60-
<a class="nav-link" role="button" (click)="logout(); collapsed = true" id="navbar-logout" tabindex="0">
61-
<fa-icon [icon]="logoutIcon" class="me-1"></fa-icon>
62-
<span translate="navbar.logout"></span>
63-
</a>
64-
} @else if (user === null) {
65-
<a class="nav-link" role="button" (click)="login(); collapsed = true" id="navbar-login" tabindex="0">
66-
<fa-icon [icon]="loginIcon" class="me-1"></fa-icon>
67-
<span translate="navbar.login"></span>
68-
</a>
33+
<div ngbDropdownMenu aria-labelledby="navbar-administration-dropdown" class="dropdown-menu">
34+
<a ngbDropdownItem routerLink="/grcs" (click)="collapsed.set(true)" id="navbar-grcs">
35+
<fa-icon [icon]="grcIcon" class="me-1"></fa-icon>
36+
<span translate="navbar.grcs"></span>
37+
</a>
38+
<a ngbDropdownItem routerLink="/accession-holders" (click)="collapsed.set(true)" id="navbar-accession-holders">
39+
<fa-icon [icon]="accessionHolderIcon" class="me-1"></fa-icon>
40+
<span translate="navbar.accession-holders"></span>
41+
</a>
42+
<a ngbDropdownItem routerLink="/users" (click)="collapsed.set(true)" id="navbar-users">
43+
<fa-icon [icon]="usersIcon" class="me-1"></fa-icon>
44+
<span translate="navbar.users"></span>
45+
</a>
46+
</div>
47+
</li>
6948
}
70-
</li>
71-
</ul>
49+
</ul>
50+
<ul class="navbar-nav align-items-baseline">
51+
@if (vm.status === 'present') {
52+
<li class="nav-item" class="me-2" id="navbar-user">
53+
<span class="navbar-text">
54+
<fa-icon [icon]="userIcon" class="me-1"></fa-icon>
55+
{{ vm.user.name }}
56+
</span>
57+
</li>
58+
}
59+
<li>
60+
@switch (vm.status) {
61+
@case ('present') {
62+
<a class="nav-link" role="button" (click)="logout(); collapsed.set(true)" id="navbar-logout" tabindex="0">
63+
<fa-icon [icon]="logoutIcon" class="me-1"></fa-icon>
64+
<span translate="navbar.logout"></span>
65+
</a>
66+
}
67+
@case ('absent') {
68+
<a class="nav-link" role="button" (click)="login(); collapsed.set(true)" id="navbar-login" tabindex="0">
69+
<fa-icon [icon]="loginIcon" class="me-1"></fa-icon>
70+
<span translate="navbar.login"></span>
71+
</a>
72+
}
73+
}
74+
</li>
75+
</ul>
76+
</div>
7277
</div>
73-
</div>
74-
</nav>
78+
</nav>
79+
}

‎frontend/src/app/navbar/navbar.component.spec.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ describe('NavbarComponent', () => {
4848
let userSubject: Subject<User>;
4949

5050
beforeEach(() => {
51-
userSubject = new Subject<User>();
51+
userSubject = new Subject<User | null>();
5252
authenticationService = createMock(AuthenticationService);
5353
authenticationService.getCurrentUser.and.returnValue(userSubject);
5454

‎frontend/src/app/navbar/navbar.component.ts

+28-14
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { Component, OnInit } from '@angular/core';
1+
import { ChangeDetectionStrategy, Component, signal } from '@angular/core';
22
import { AuthenticationService } from '../shared/authentication.service';
33
import {
44
faBuilding,
@@ -15,18 +15,33 @@ import { TranslateModule } from '@ngx-translate/core';
1515
import { FontAwesomeModule } from '@fortawesome/angular-fontawesome';
1616
import { RouterLink } from '@angular/router';
1717

18-
import { NgbDropdown, NgbDropdownItem, NgbDropdownMenu, NgbDropdownToggle } from '@ng-bootstrap/ng-bootstrap';
18+
import { NgbCollapse, NgbDropdown, NgbDropdownItem, NgbDropdownMenu, NgbDropdownToggle } from '@ng-bootstrap/ng-bootstrap';
19+
import { map, Observable, startWith } from 'rxjs';
20+
import { AsyncPipe } from '@angular/common';
21+
22+
type ViewModel = { status: 'unknown' | 'absent' } | { status: 'present'; user: User };
1923

2024
@Component({
2125
selector: 'rb-navbar',
2226
templateUrl: './navbar.component.html',
2327
styleUrl: './navbar.component.scss',
2428
standalone: true,
25-
imports: [RouterLink, FontAwesomeModule, TranslateModule, NgbDropdown, NgbDropdownToggle, NgbDropdownMenu, NgbDropdownItem]
29+
imports: [
30+
RouterLink,
31+
FontAwesomeModule,
32+
TranslateModule,
33+
NgbDropdown,
34+
NgbDropdownToggle,
35+
NgbDropdownMenu,
36+
NgbDropdownItem,
37+
AsyncPipe,
38+
NgbCollapse
39+
],
40+
changeDetection: ChangeDetectionStrategy.OnPush
2641
})
27-
export class NavbarComponent implements OnInit {
28-
collapsed = true;
29-
user: User | null | undefined = undefined;
42+
export class NavbarComponent {
43+
collapsed = signal(true);
44+
vm$: Observable<ViewModel>;
3045

3146
ordersIcon = faShoppingBag;
3247
administrationIcon = faShieldAlt;
@@ -37,12 +52,11 @@ export class NavbarComponent implements OnInit {
3752
loginIcon = faSignInAlt;
3853
logoutIcon = faPowerOff;
3954

40-
constructor(private authenticationService: AuthenticationService) {}
41-
42-
ngOnInit() {
43-
this.authenticationService.getCurrentUser().subscribe(user => {
44-
this.user = user;
45-
});
55+
constructor(private authenticationService: AuthenticationService) {
56+
this.vm$ = this.authenticationService.getCurrentUser().pipe(
57+
map((user): ViewModel => (user ? { status: 'present', user } : { status: 'absent' })),
58+
startWith({ status: 'unknown' as const })
59+
);
4660
}
4761

4862
login() {
@@ -53,7 +67,7 @@ export class NavbarComponent implements OnInit {
5367
this.authenticationService.logout();
5468
}
5569

56-
hasPermission(permission: Permission): boolean {
57-
return this.user && this.user.permissions.includes(permission);
70+
hasPermission(vm: ViewModel, permission: Permission): boolean {
71+
return vm.status === 'present' && vm.user.permissions.includes(permission);
5872
}
5973
}

0 commit comments

Comments
 (0)