Skip to content

Commit 7797aeb

Browse files
committed
Merge branch 'feature/logout' into develop
2 parents 9d7be8c + 2d41b22 commit 7797aeb

File tree

12 files changed

+169
-34
lines changed

12 files changed

+169
-34
lines changed

src/app/core/guards/user-data.guard.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,6 @@ export class UserDataGuard implements CanActivateChild {
2020
if (! user) {
2121
this.store.dispatch(getCurrentUserAction());
2222
}
23-
console.log(user);
2423
return true;
2524
})
2625
);

src/app/modules/authentication/services/auth.service.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,6 @@ export class AuthService {
3434
}
3535

3636
public getCurrentUser(): Observable<User> {
37-
console.log('getCurrentUser');
3837
return this.http.get<User>(`${environment.apiUrl}/user/me`);
3938
}
4039

src/app/modules/authentication/store/auth.effects.ts

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -70,10 +70,7 @@ export class AuthEffects {
7070
this.actions$.pipe(
7171
ofType(AuthActions.getCurrentUserAction),
7272
switchMap(() => this.authService.getCurrentUser().pipe(
73-
map(({ data }: any) => {
74-
console.log(data);
75-
return AuthActions.fetchCurrentUserSuccessAction({ user: data.user })
76-
}),
73+
map(({ data }: any) => AuthActions.fetchCurrentUserSuccessAction({ user: data.user })),
7774
catchError(() => EMPTY)
7875
))
7976
)

src/app/modules/authentication/store/auth.reducer.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ export const authReducer = createReducer(
2020
AuthActions.authenticateAction,
2121
AuthActions.forgotPasswordAction,
2222
AuthActions.resetPasswordAction,
23+
AuthActions.logoutAction,
2324
(state: AuthState): AuthState => {
2425
return {
2526
...state,

src/app/modules/user/interfaces/user.interface.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ export interface User {
2121
email: string;
2222
phoneNumber: string;
2323
accountType: string;
24+
profilePhotoUrl: string;
2425
timezone: string;
2526
emailVerifiedAt: string;
2627
roles: string[];
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
import { Directive, ElementRef, EventEmitter, HostListener, Output } from '@angular/core';
2+
3+
@Directive({
4+
selector: '[clickOutside]'
5+
})
6+
export class ClickOutsideDirective {
7+
8+
constructor(private elementRef: ElementRef) { }
9+
10+
@Output() clickOutside = new EventEmitter<MouseEvent>();
11+
12+
@HostListener('document:click', ['$event', '$event.target'])
13+
public onClick(event: MouseEvent, targetElement: HTMLElement): void {
14+
if (!targetElement) {
15+
return;
16+
}
17+
18+
const clickedInside = this.elementRef.nativeElement.contains(targetElement);
19+
20+
if (!clickedInside) {
21+
this.clickOutside.emit(event);
22+
}
23+
}
24+
25+
}

src/app/shared/shared.module.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import { OverlapingLabelComponent } from './components/inputs/overlaping-label/o
88
import { PrimaryComponent as ButtonPrimary } from './components/buttons/primary/primary.component';
99
import { ErrorComponent } from './components/alert/error/error.component';
1010
import { SuccessComponent } from './components/alert/success/success.component';
11+
import { ClickOutsideDirective } from './directives/click-outside.directive';
1112

1213

1314
const MODULES = [CommonModule, ThemeModule];
@@ -16,6 +17,7 @@ const DECLARATIONS = [
1617
OverlapingLabelComponent,
1718
ErrorComponent,
1819
SuccessComponent,
20+
ClickOutsideDirective,
1921
];
2022

2123
@NgModule({
Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
<header class="p-6 border-b border-slate-100 sm:px-8">
2+
<div class="flex items-center justify-between">
3+
<div class="max-w-lg">
4+
<!-- SearchBar Component -->
5+
</div>
6+
<div class="flex items-center divide-x divide-slate-200">
7+
<a href="#" class="flex items-center pr-4 text-sm leading-5 text-slate-500 hover:text-slate-700">
8+
<svg class="w-6 h-6 mr-2 text-slate-400" stroke="currentColor" fill="none" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
9+
<path d="M2.5 12h3.382c.685 0 1.312.387 1.618 1 .306.613.933 1 1.618 1h5.764c.685 0 1.312-.387 1.618-1 .306-.613.933-1 1.618-1H21.5M8.967 4h6.066c1.077 0 1.616 0 2.091.164a3 3 0 0 1 1.121.693c.36.352.6.833 1.082 1.796l2.166 4.333c.19.378.284.567.35.765.06.177.102.357.128.541.029.207.029.418.029.841V15.2c0 1.68 0 2.52-.327 3.162a3 3 0 0 1-1.311 1.311C19.72 20 18.88 20 17.2 20H6.8c-1.68 0-2.52 0-3.162-.327a3 3 0 0 1-1.311-1.311C2 17.72 2 16.88 2 15.2v-2.067c0-.422 0-.634.029-.84.026-.184.068-.365.128-.541.066-.199.16-.388.35-.766l2.166-4.333c.482-.963.723-1.444 1.082-1.796a3 3 0 0 1 1.12-.693C7.352 4 7.89 4 8.968 4Z"/>
10+
</svg>
11+
Vous rencontrez un problème ?
12+
</a>
13+
<div class="flex items-center pl-4 space-x-3">
14+
<button type="button" class="inline-flex items-center p-1 text-sm leading-5 rounded-full hover:bg-slate-50 text-slate-500 hover:text-slate-900 focus:outline-none">
15+
<svg class="w-6 h-6" stroke="currentColor" fill="none" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
16+
<path d="M9.354 21c.705.622 1.632 1 2.646 1s1.94-.378 2.646-1M18 8A6 6 0 1 0 6 8c0 3.09-.78 5.206-1.65 6.605-.735 1.18-1.102 1.771-1.089 1.936.015.182.054.252.2.36.133.099.732.099 1.928.099H18.61c1.196 0 1.795 0 1.927-.098.147-.11.186-.179.2-.361.014-.165-.353-.755-1.088-1.936C18.78 13.206 18 11.09 18 8Z"/>
17+
</svg>
18+
</button>
19+
<div class="relative" #menuDropdown>
20+
<button
21+
type="button"
22+
class="flex items-center text-sm rounded-full focus:outline-none"
23+
id="user-menu-button"
24+
aria-expanded="false"
25+
aria-haspopup="true"
26+
(click)="toggleMobileMenu()"
27+
>
28+
<span class="sr-only">Open user menu</span>
29+
<img class="w-8 h-8 rounded-full" *ngIf="user$ | async; let user" [src]="user.profilePhotoUrl" [alt]="user.name">
30+
</button>
31+
<div
32+
[@openClose]="openCloseTrigger"
33+
class="absolute right-0 z-10 w-56 mt-2 origin-top-right bg-white divide-y rounded-md shadow-lg divide-slate-100 ring-1 ring-black ring-opacity-5 focus:outline-none"
34+
role="menu"
35+
aria-orientation="vertical"
36+
aria-labelledby="menu-button"
37+
tabindex="-1"
38+
>
39+
<div class="px-4 py-3" role="none">
40+
<p class="text-sm" role="none">Connecté avec</p>
41+
<p class="text-sm font-medium truncate text-slate-900" role="none" *ngIf="user$ | async; let user">{{ user.email }}</p>
42+
</div>
43+
<div class="py-1" role="none">
44+
<a href="#" class="block px-4 py-2 text-sm text-slate-700 hover:bg-slate-100 hover:text-slate-900" role="menuitem" tabindex="-1" id="menu-item-0">Mon compte</a>
45+
<a href="#" class="block px-4 py-2 text-sm text-slate-700 hover:bg-slate-100 hover:text-slate-900" role="menuitem" tabindex="-1" id="menu-item-1">Support</a>
46+
<a href="#" class="block px-4 py-2 text-sm text-slate-700 hover:bg-slate-100 hover:text-slate-900" role="menuitem" tabindex="-1" id="menu-item-2">Guide d'utilisation</a>
47+
</div>
48+
<div class="flex items-center justify-between py-1" role="none">
49+
<button type="button" (click)="logout()" class="block w-full px-4 py-2 text-sm text-left text-slate-700" role="menuitem" tabindex="-1" id="menu-item-3">Se déconnecter</button>
50+
<span *ngIf="(loading$ | async)" class="flex items-center px-3">
51+
<svg class="w-5 h-5 mr-3 -ml-1 text-green-500 animate-spin" fill="none" viewBox="0 0 24 24">
52+
<circle class="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" stroke-width="4"/>
53+
<path class="opacity-75" fill="currentColor" d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"/>
54+
</svg>
55+
</span>
56+
</div>
57+
</div>
58+
</div>
59+
</div>
60+
</div>
61+
</div>
62+
</header>

src/app/shared/themes/components/header/header.component.scss

Whitespace-only changes.
Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
import { Store } from '@ngrx/store';
2+
import { Observable } from 'rxjs';
3+
import { Component, ElementRef, HostListener, ViewChild } from '@angular/core';
4+
import {
5+
animate,
6+
state,
7+
style,
8+
transition,
9+
trigger
10+
} from '@angular/animations';
11+
12+
import { User } from '@app/modules/user/interfaces/user.interface';
13+
import { selectCurrentUser, selectLoading } from '@app/modules/authentication/store/auth.selectors';
14+
import { logoutAction } from '@app/modules/authentication/store/auth.actions';
15+
16+
@Component({
17+
selector: 'cosna-header',
18+
templateUrl: './header.component.html',
19+
styleUrls: ['./header.component.scss'],
20+
animations: [
21+
trigger('openClose', [
22+
state(
23+
'open',
24+
style({
25+
opacity: 1,
26+
transform: 'scale(1, 1)'
27+
})
28+
),
29+
state(
30+
'closed',
31+
style({
32+
opacity: 0,
33+
transform: 'scale(0.95, 0.95)'
34+
})
35+
),
36+
transition('open => closed', [animate('100ms ease-in')]),
37+
transition('closed => open', [animate('200ms ease-out')])
38+
])
39+
]
40+
})
41+
export class HeaderComponent {
42+
43+
mobileMenuOpen!: boolean;
44+
45+
@ViewChild('menuDropdown') menuDropdown!: ElementRef;
46+
47+
public user$: Observable<User | null> = this.store.select(selectCurrentUser);
48+
49+
public loading$: Observable<boolean> = this.store.select(selectLoading);
50+
51+
public logout() {
52+
this.store.dispatch(logoutAction());
53+
}
54+
55+
get openCloseTrigger() {
56+
return this.mobileMenuOpen ? 'open' : 'closed';
57+
}
58+
59+
toggleMobileMenu(): void {
60+
this.mobileMenuOpen = !this.mobileMenuOpen;
61+
}
62+
63+
@HostListener('document:click', ['$event.target'])
64+
public onPageClick(targetElement: HTMLElement): void {
65+
const clickedInside = this.menuDropdown.nativeElement.contains(targetElement);
66+
67+
if (!clickedInside) {
68+
this.mobileMenuOpen = false;
69+
}
70+
}
71+
72+
constructor(private store: Store) { }
73+
74+
}

0 commit comments

Comments
 (0)