Skip to content

Commit 374d3ce

Browse files
shawonshawon
authored andcommitted
added profile menu to the navigation sidebar
1 parent 96674ca commit 374d3ce

File tree

5 files changed

+287
-76
lines changed

5 files changed

+287
-76
lines changed

eform-client/cypress/e2e/Navbar.page.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ export class Navbar {
88
}
99

1010
public signOutDropdown() {
11-
return cy.get('#sign-out-dropdown').should('be.visible').click();
11+
return cy.get('#sign-out-dropdown').should('be.visible');
1212
}
1313

1414
public advancedBtn() {
@@ -96,7 +96,7 @@ export class Navbar {
9696

9797
public goToProfileSettings() {
9898
this.signOutDropdown().click();
99-
this.settingsBtn().should('be.visible').should('be.enabled').click();
99+
this.settingsBtn().should('be.visible').should('be.enabled').click({ force: true });
100100
// Note: Tests should intercept appropriate API calls after navigation
101101
cy.wait(500);
102102
}
Lines changed: 27 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,30 @@
1-
<footer class="footer stick-down">
2-
<div id="footer-widgets">
3-
<br>
4-
<div class="d-flex justify-content-center">
5-
<a href="https://www.microting.com/terms-of-service" target="_blank">
6-
{{'Terms of Service' | translate}}
7-
</a>
8-
</div>
9-
<div class="d-flex justify-content-center">
10-
<a href="https://www.microting.com/privacy-policy" target="_blank">
11-
{{'Privacy Policy' | translate}}
12-
</a>
13-
</div>
14-
<div class="d-flex justify-content-center">
15-
<a href="https://www.microting.com/data-protection-terms" target="_blank">
16-
{{'Data Protection Terms (GDPR)' | translate}}
17-
</a>
18-
<b></b>
1+
<footer class="footer stick-down" [matMenuTriggerFor]="profileMenu" id="sign-out-dropdown">
2+
<div class="profile-block">
3+
<img class="avatar" [src]="avatarUrl" alt="avatar"/>
4+
<div class="info">
5+
<div class="name">{{ fullName }}</div>
6+
<div class="email">{{ userName }}</div>
197
</div>
8+
<mat-icon class="expand-icon">expand_more</mat-icon>
209
</div>
21-
<div class="footer-copyright text-center p-2 copyright-section">
22-
<div>© Microting A/S 2007 - {{date.getFullYear()}} <div class="footer-version">eForm Backend v{{version}}</div> </div>
23-
</div>
10+
11+
<mat-menu #profileMenu="matMenu" class="profile-menu-panel">
12+
<div class="menu-header">
13+
<img class="avatar-lg" [src]="avatarUrl"/>
14+
<div>
15+
<div class="name">{{ fullName }}</div>
16+
<div class="email">{{ userName }}</div>
17+
</div>
18+
</div>
19+
<ng-container *ngFor="let menu of allAppMenus$ | async">
20+
<ng-container *ngIf="checkGuards(menu.guards) | async">
21+
<a mat-menu-item
22+
routerLink="{{ menu.link }}"
23+
[id]="menu.e2EId"
24+
>
25+
{{ menu.name }}
26+
</a>
27+
</ng-container>
28+
</ng-container>
29+
</mat-menu>
2430
</footer>
Lines changed: 174 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,36 +1,190 @@
11
@use "../../../scss/utilities/colors" as *;
22

3-
footer {
4-
color: $eform-gray;
3+
body, .theme-light {
4+
--black-800: #242729;
5+
--blue-600: #0077cc;
6+
--theme-body-font-color: var(--black-800);
7+
--tp-td-bg: #ffffff;
8+
--tp-th-bg: #f7f9fa;
9+
--tp-border: #EBEFF2;
10+
--tp-text: #111827;
11+
--tp-white-text: #0F1316;
12+
--border: #{$border-light};
13+
--icon-secondary: #{$icon-secondary-light};
14+
--bg: #{$bg-light};
15+
--text-header: #{$text-header-light};
16+
//--mat-card-elevated-container-color: #{$bg-light}!important;
17+
--primary: #{$primary-light};
18+
--primary-light: #{$primary-light-light-mode};
19+
--warning: #{$warning-light};
20+
--error: #{$error-light};
21+
--btn-delete-text: #{$text-header-dark};
22+
--text-body: #{$text-body-light};
23+
--card: #{$card-light};
24+
--mat-table-row-item-outline-color: #{$border-light}!important;
25+
--mat-card-elevated-container-color: #{$bg-light}!important;
526
}
627

7-
#footer-widgets {
8-
background-color: $eform-gray;
9-
font-size: 0.7rem;
28+
body, .theme-dark {
29+
--tp-td-bg: #424242;
30+
--tp-th-bg: #424242;
31+
--tp-border: #2B2B2B;
32+
--tp-text: #E6E6E6;
33+
--tp-white-text: white;
34+
--border: #{$border-dark};
35+
--icon-secondary: #{$icon-secondary-dark};
36+
--bg: #{$bg-dark};
37+
--text-header: #{$text-header-dark};
38+
//--mat-card-elevated-container-color: #{$bg-dark}!important;
39+
--primary: #{$primary-dark};
40+
--primary-light: #{$primary-light-dark-mode};
41+
--warning: #{$warning-dark};
42+
--error: #{$error-dark};
43+
--btn-delete-text: #{$text-header-light};
44+
--text-body: #{$text-body-dark};
45+
--card: #{$card-dark};
46+
--mat-table-row-item-outline-color: #{$border-dark}!important;
47+
--mat-card-elevated-container-color: #{$bg-dark}!important;
1048
}
1149

12-
a {
13-
color: white;
50+
51+
.footer {
52+
display: flex;
53+
padding: var(--416-px, 16px);
54+
align-items: center;
55+
gap: var(--28-px, 8px);
56+
align-self: stretch;
57+
background: var(--bg);
58+
border-top: 1px solid var(--border, #EBEFF2);
59+
&:hover {
60+
background: var(--primary-light, #F5FCFC);
61+
color: var(--primary, #289694);
62+
cursor: pointer;
63+
}
1464
}
1565

16-
span {
17-
color: white;
66+
.profile-block {
67+
display: flex;
68+
align-items: flex-start;
69+
justify-content: space-between;
70+
gap: var(--28-px, 8px);
71+
align-self: stretch;
72+
cursor: pointer;
73+
padding: 0;
74+
background: transparent;
75+
76+
&:hover {
77+
background: transparent;
78+
}
1879
}
1980

20-
.copyright-section {
21-
background-color: $eform-gray !important;
22-
color: white;
23-
font-size: 0.7rem;
81+
.avatar {
82+
width: 40px;
83+
height: 40px;
84+
aspect-ratio: 1/1;
85+
border-radius: var(--rounded-full, 9999px);
86+
}
87+
88+
.info {
89+
flex-grow: 1;
90+
91+
.name {
92+
align-self: stretch;
93+
color: var(--text-header, #0F1316);
94+
font-family: var(--font-family, Poppins);
95+
font-size: var(--Text-size-sm, 14px);
96+
font-style: normal;
97+
font-weight: 500;
98+
line-height: var(--Text-line-height-sm, 22px); /* 157.143% */
99+
}
100+
.email {
101+
align-self: stretch;
102+
color: var(--text-body, #7F868D);
103+
font-family: var(--Fonts-Font-family-Text, Inter);
104+
font-size: var(--Text-font-size-Text-xs, 12px);
105+
font-style: normal;
106+
font-weight: 400;
107+
line-height: var(--Text-line-height-Text-xs, 18px); /* 150% */
108+
}
24109
}
25110

26-
.stick-down {
27-
//left: 0;
28-
//bottom: 0;
111+
.expand-icon {
112+
font-size: 20px;
113+
opacity: 0.6;
114+
}
115+
116+
.profile-menu-panel {
29117
width: 100%;
30-
//position: absolute;
118+
background: var(--bg);
119+
padding: 0!important;
120+
}
121+
122+
.menu-header {
123+
display: flex;
124+
align-items: center;
125+
gap: var(--28-px, 8px);
126+
align-self: stretch;
127+
padding: var(--28-px, 8px) var(--416-px, 14px);
128+
border-bottom: 1px solid var(--border, #EBEFF2);
129+
130+
.avatar-lg {
131+
width: 40px;
132+
height: 40px;
133+
aspect-ratio: 1/1;
134+
border-radius: var(--rounded-full, 9999px);
135+
}
136+
137+
.name {
138+
align-self: stretch;
139+
color: var(--text-header, #0F1316);
140+
font-family: var(--font-family, Poppins);
141+
font-size: var(--Text-size-sm, 14px);
142+
font-style: normal;
143+
font-weight: 500;
144+
line-height: var(--Text-line-height-sm, 22px); /* 157.143% */
145+
}
146+
.email {
147+
align-self: stretch;
148+
color: var(--text-body, #7F868D);
149+
font-family: var(--Fonts-Font-family-Text, Inter);
150+
font-size: var(--Text-font-size-Text-xs, 12px);
151+
font-style: normal;
152+
font-weight: 400;
153+
line-height: var(--Text-line-height-Text-xs, 18px); /* 150% */
154+
}
155+
31156
}
32157

33-
.footer-version {
34-
color: gray;
35-
//font-size: 1em;
158+
a{
159+
display: flex;
160+
padding: var(--28-px, 8px) var(--416-px, 16px);
161+
align-items: center;
162+
gap: var(--28-px, 8px);
163+
align-self: stretch;
164+
color: var(--text-header, #0F1316)!important;
165+
166+
font-family: var(--font-family, "Nunito Sans");
167+
font-size: var(--Text-size-sm, 14px);
168+
font-style: normal;
169+
font-weight: 600;
170+
line-height: var(--Text-line-height-sm, 22px);
171+
172+
&:hover {
173+
background: var(--primary-light, #F5FCFC);
174+
color: var(--primary, #289694);
175+
}
176+
}
177+
178+
.mat-mdc-menu-content{
179+
display: inline-flex;
180+
padding: var(--28-px, 8px);
181+
flex-direction: column;
182+
align-items: flex-start;
183+
gap: var(--14-px, 4px);
184+
border-radius: var(--rounded-8, 8px);
185+
border: 1px solid var(--border, #EBEFF2);
186+
background: var(--bg, #FFF);
187+
box-shadow: 0 4px 20px 0 rgba(0, 0, 0, 0.10);
188+
189+
36190
}
Lines changed: 66 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,27 +1,78 @@
1-
import { Component, OnInit, inject } from '@angular/core';
2-
import {AppSettingsService} from 'src/app/common/services';
1+
import { Component, inject, OnInit, DestroyRef } from '@angular/core';
2+
import { Store } from '@ngrx/store';
3+
import {
4+
selectCurrentUserFullName,
5+
selectCurrentUserName,
6+
selectCurrentUserAvatarUrl,
7+
leftAppMenus, selectCurrentUserClaims, rightAppMenus
8+
} from 'src/app/state';
9+
10+
import { MatMenu, MatMenuTrigger } from '@angular/material/menu';
11+
import { MatIcon } from '@angular/material/icon';
12+
import { RouterLink } from '@angular/router';
13+
import { AsyncPipe, NgIf, NgForOf } from '@angular/common';
14+
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
15+
import {Observable} from "rxjs";
16+
import {map} from "rxjs/operators";
17+
import {snakeToCamel} from "src/app/common/helpers";
18+
319

420
@Component({
5-
selector: 'app-footer',
6-
templateUrl: './footer.component.html',
7-
styleUrls: ['./footer.component.scss'],
8-
standalone: false
21+
selector: 'app-footer',
22+
standalone: true,
23+
templateUrl: './footer.component.html',
24+
styleUrls: ['./footer.component.scss'],
25+
imports: [
26+
MatMenuTrigger,
27+
MatMenu,
28+
MatIcon,
29+
RouterLink,
30+
AsyncPipe,
31+
NgIf,
32+
NgForOf
33+
]
934
})
1035
export class FooterComponent implements OnInit {
11-
private settingsService = inject(AppSettingsService);
36+
private store = inject(Store);
37+
private authStore = inject(Store);
38+
private destroyRef = inject(DestroyRef);
39+
private selectCurrentUserClaims$ = this.authStore.select(selectCurrentUserClaims);
1240

13-
version: string;
14-
date = new Date();
41+
fullName = '';
42+
userName = '';
43+
avatarUrl = '';
44+
45+
public allAppMenus$ = this.store.select(rightAppMenus);
1546

1647
ngOnInit() {
17-
this.getAssemblyVersion();
48+
this.store.select(selectCurrentUserFullName)
49+
.pipe(takeUntilDestroyed(this.destroyRef))
50+
.subscribe(name => this.fullName = name);
51+
52+
this.store.select(selectCurrentUserName)
53+
.pipe(takeUntilDestroyed(this.destroyRef))
54+
.subscribe(user => this.userName = user);
55+
56+
this.store.select(selectCurrentUserAvatarUrl)
57+
.pipe(takeUntilDestroyed(this.destroyRef))
58+
.subscribe(url => this.avatarUrl = url);
1859
}
1960

20-
getAssemblyVersion() {
21-
this.settingsService.getAssemblyVersion().subscribe(operation => {
22-
if (operation && operation.success) {
23-
this.version = operation.model;
61+
checkGuards(guards: string[]): Observable<boolean> {
62+
if (guards.length === 0) {
63+
return new Observable<boolean>(x => x.next(true));
64+
}
65+
return this.selectCurrentUserClaims$.pipe(map(x => {
66+
for (const guard of guards) {
67+
if (x[snakeToCamel(guard)]) {
68+
return true;
69+
}
2470
}
25-
});
71+
return false;
72+
}));
73+
}
74+
75+
logout() {
76+
// add logout logic here
2677
}
2778
}

0 commit comments

Comments
 (0)