diff --git a/src/app/header/header.component.html b/src/app/header/header.component.html index 42d784193798e81d1137ed7d98bdca0d504984f6..943465ebdacbe84b875b01f23382d596baedf1cd 100644 --- a/src/app/header/header.component.html +++ b/src/app/header/header.component.html @@ -1,4 +1,7 @@ <div fxLayout="row" class="header hide-on-print"> + <div class="containerIconMenu"> + <app-svg-icon (click)="openMenu()" [type]="'ico'" [icon]="'menu'" [iconClass]="'icon-32'"></app-svg-icon> + </div> <div class="logo clickable" routerLink="/home"> <img *ngIf="displayLogo(); else customTitle" @@ -10,7 +13,12 @@ /> </div> <div class="containerIconMenu"> - <app-svg-icon (click)="openMenu()" [type]="'ico'" [icon]="'menu'" [iconClass]="'icon-32'"></app-svg-icon> + <app-svg-icon + (click)="isLoggedIn ? openProfileMenu() : goToLoginPage()" + [type]="'ico'" + [icon]="'profile'" + [iconClass]="'icon-32'" + ></app-svg-icon> </div> <div fxLayout="row" class="right-header" fxLayoutAlign="center center" fxLayoutGap="3vw"> <a routerLink="/news" [routerLinkActive]="'active'" i18n>Actualités</a> @@ -18,7 +26,7 @@ <a routerLink="/orientation" [routerLinkActive]="'active'" i18n>Orienter un bénéficiaire</a> <a routerLink="/page/qui-sommes-nous" [routerLinkActive]="'active'" i18n>Qui sommes-nous ?</a> <a *ngIf="isAdmin" routerLink="/admin" [routerLinkActive]="'active'">Administration</a> - <button *ngIf="isLoggedIn" class="red" routerLink="/profile" [routerLinkActive]="'active'"> + <button *ngIf="isLoggedIn" class="red" (click)="openProfileMenu()"> {{ displayName }} </button> <app-button @@ -29,8 +37,8 @@ ></app-button> </div> </div> -<div *ngIf="showMenu" class="mobile-menu" fxLayout="row"> - <div class="outside" (click)="closeMenu()"></div> + +<div *ngIf="showMenu" class="mobile-menu" [@slideInOut] fxLayout="row"> <div class="menu-content" fxLayout="column" fxLayoutAlign="space-between"> <div> <div class="title" fxLayout="row" fxLayoutAlign="space-between center"> @@ -49,12 +57,35 @@ <a *ngIf="isAdmin" routerLink="/admin" [routerLinkActive]="'active'" (click)="closeMenu()">Administration</a> </div> </div> - <div> - <button *ngIf="isLoggedIn" class="red" routerLink="/profile" [routerLinkActive]="'active'" (click)="closeMenu()"> - {{ displayName }} - </button> - <button *ngIf="!isLoggedIn" (click)="isPopUpOpen = !isPopUpOpen; closeMenu()">Se connecter</button> + </div> + <div class="outside" (click)="closeMenu()"></div> +</div> + +<div *ngIf="showProfileMenu && isLoggedIn" [@toogle] class="profileModal"> + <div class="overlay" (click)="closeProfileMenu()"> + <div class="overlay-header"></div> + <div class="overlay-content" [@fadeInOut]></div> + </div> + <div class="profileMenu" [@fadeInOut]> + <div class="profileInformation"> + <app-svg-icon + class="avatar" + (click)="isLoggedIn ? openProfileMenu() : goToLoginPage()" + [type]="'avatar'" + [icon]="'defaultAvatar'" + [iconClass]="'icon-40'" + ></app-svg-icon> + <span class="name">{{ displayFullName }}</span> </div> + <app-button + class="firstBtn" + [text]="'Voir mon compte'" + [style]="buttonTypeEnum.SecondaryWide" + (click)="closeProfileMenu()" + routerLink="/profile" + [routerLinkActive]="'active'" + ></app-button> + <app-button (action)="logout()" [text]="'Se déconnecter'" [style]="buttonTypeEnum.SecondaryWide"></app-button> </div> </div> diff --git a/src/app/header/header.component.scss b/src/app/header/header.component.scss index f36e34c5115535a11357d5f8c6234b2c5da374aa..5dc882c7133c7966cfccc25e1bcaa9248b7e340f 100644 --- a/src/app/header/header.component.scss +++ b/src/app/header/header.component.scss @@ -34,7 +34,6 @@ @media #{$tablet} { margin-right: unset; text-align: center; - margin-left: 32px; width: 100%; } } @@ -88,30 +87,15 @@ a { } } -@keyframes slideMenu { - from { - width: 0; - } - to { - width: 100%; - } -} -@keyframes background-fade { - 0% { - background: none; - } - 100% { - background: none; - } -} .mobile-menu { position: fixed; top: 0; - right: 0; - width: 100%; + left: 0; + width: calc(100% + 350px); height: 100%; z-index: $menu-phone-z-index; - animation: slideMenu 0.5s; + background-color: $modal-background; + .menu-content { background-color: $white; height: calc(var(--vh, 1vh) * 100); @@ -119,11 +103,10 @@ a { padding: 27px 25px; } .outside { - width: calc(100% - 350px); + position: absolute; + left: 350px; + width: 100vw; height: 100vh; - opacity: 0.65; - animation: background-fade 0.1s; - background-color: $black; } } .title { @@ -134,6 +117,67 @@ a { } } +.profileModal { + .overlay { + position: absolute; + inset: 0; + z-index: $modal-z-index; + display: flex; + flex-direction: column; + .overlay-header { + height: $header-height; + background-color: $modal-background-transparent; + } + .overlay-content { + flex-grow: 1; + background-color: $modal-background; + } + } + + .profileMenu { + position: absolute; + top: 8 + $header-height; + z-index: $modal-z-index; + right: 8px; + width: 184px; + display: flex; + flex-direction: column; + align-items: flex-start; + justify-content: center; + padding: 16px 8px 8px; + background: $white; + box-shadow: $menu-shadow; + border-radius: 8px; + + .profileInformation { + display: flex; + align-items: center; + margin-bottom: 8px; + width: 100%; + .avatar { + flex-shrink: 0; + width: 40px; + height: 44px; + background: $grey-8; + border-radius: 4px; + } + .name { + @include lato-bold-16; + margin-left: 10px; + text-transform: capitalize; + display: -webkit-box; + -webkit-line-clamp: 2; + -webkit-box-orient: vertical; + overflow: hidden; + } + } + + .firstBtn { + margin-bottom: 6px; + } + } +} + .desktop-show { display: block; @media #{$tablet} { diff --git a/src/app/header/header.component.ts b/src/app/header/header.component.ts index 66be2616f8eaaa0adef4b508501ac302a709b051..111f943b6075cb8e25b35b018355bc6fbaae53f1 100644 --- a/src/app/header/header.component.ts +++ b/src/app/header/header.component.ts @@ -1,5 +1,6 @@ +import { animate, animateChild, query, style, transition, trigger } from '@angular/animations'; import { Component, OnInit } from '@angular/core'; -import { ActivatedRoute, NavigationEnd, Router } from '@angular/router'; +import { NavigationEnd, Router } from '@angular/router'; import { Structure } from '../models/structure.model'; import { ProfileService } from '../profile/services/profile.service'; import { AuthService } from '../services/auth.service'; @@ -9,9 +10,29 @@ import { ButtonType } from '../shared/components/button/buttonType.enum'; selector: 'app-header', templateUrl: './header.component.html', styleUrls: ['./header.component.scss'], + animations: [ + trigger('slideInOut', [ + transition(':enter', [ + style({ left: '-350px', backgroundColor: 'rgb(51, 51, 51, 0)' }), + animate('250ms ease-in', style({ left: '0', backgroundColor: 'rgb(51, 51, 51, 0.25)' })), + ]), + transition(':leave', [ + animate('250ms ease-in', style({ left: '-350px', backgroundColor: 'rgb(51, 51, 51, 0)' })), + ]), + ]), + trigger('toogle', [ + transition(':enter', [query('@fadeInOut', animateChild())]), + transition(':leave', [query('@fadeInOut', animateChild())]), + ]), + trigger('fadeInOut', [ + transition(':enter', [style({ opacity: '0' }), animate('200ms ease-in', style({ opacity: '1' }))]), + transition(':leave', [animate('200ms ease-in', style({ opacity: '0' }))]), + ]), + ], }) export class HeaderComponent implements OnInit { public showMenu = false; + public showProfileMenu = false; public currentRoute = ''; public formRoute = '/create-structure'; public returnUrl = null; @@ -20,31 +41,14 @@ export class HeaderComponent implements OnInit { private loadingDataShare = false; public buttonTypeEnum = ButtonType; - constructor( - private authService: AuthService, - private profileService: ProfileService, - private router: Router, - private route: ActivatedRoute - ) { + constructor(private authService: AuthService, private profileService: ProfileService, private router: Router) { this.router.events.subscribe((event) => { if (event instanceof NavigationEnd) { this.currentRoute = event.url; } }); } - ngOnInit(): void { - // this.route.queryParams.subscribe((params) => { - // if (params.verified || params.returnUrl) { - // Promise.resolve().then(() => { - // if (!this.isLoggedIn) { - // this.isPopUpOpen = true; - // this.displaySignUp = true; - // } - // }); - // this.returnUrl = params.returnUrl; - // } - // }); - } + ngOnInit(): void {} public openMenu(): void { this.showMenu = true; @@ -53,6 +57,13 @@ export class HeaderComponent implements OnInit { this.showMenu = false; } + public openProfileMenu(): void { + this.showProfileMenu = true; + } + public closeProfileMenu(): void { + this.showProfileMenu = false; + } + public get isLoggedIn(): boolean { return this.authService.isLoggedIn(); } @@ -85,7 +96,15 @@ export class HeaderComponent implements OnInit { return this.authService.getUsernameDisplay(); } + public get displayFullName(): string { + return this.authService.getUserfullnameDisplay(); + } + public displayLogo(): boolean { return this.formRoute !== this.currentRoute; } + + public logout(): void { + this.authService.logout(); + } } diff --git a/src/app/models/user.model.ts b/src/app/models/user.model.ts index 29b6f3b96fa1937e6dd49a6ae415f5e5ca501211..d8b64e6c047b02fd704de4998b79ec09c068a7a4 100644 --- a/src/app/models/user.model.ts +++ b/src/app/models/user.model.ts @@ -17,6 +17,7 @@ export class User { personalOffers: string[] = []; job: Job; employer: Employer; + description?: string; constructor(obj?: any) { Object.assign(this, obj); } diff --git a/src/app/profile/profile.component.html b/src/app/profile/profile.component.html index 8e2b6f279238bec23c2ecb84c55df6c7cc1d7d4f..ecb9c9bebc600f9901f62f921a20058b109fa2d1 100644 --- a/src/app/profile/profile.component.html +++ b/src/app/profile/profile.component.html @@ -1,58 +1,113 @@ -<div fxLayout="column" class="content-container full-screen" *ngIf="userProfile"> - <div class="profileSection"> - <div class="section-container" fxLayout="row"> - <ng-container *ngIf="userProfile.profileImage"> - <img class="cameraProfile" alt="image of profile" src="userProfile.profileImage" /> - </ng-container> - <div class="profileInformation" fxLayoutGap="18px" fxLayout="column"> - <div fxLayout="row" fxLayoutAlign="space-between center"> - <p class="profileName">{{ userProfile.name | titlecase }} {{ userProfile.surname | titlecase }}</p> - <button class="btn-primary" (click)="logout()">Déconnexion</button> - </div> - <div class="profileEmail" fxLayout="column"> - <span>Identifiant</span> - <div fxLayout="row" fxLayoutAlign="space-between center"> - <p>{{ userProfile.email }}</p> - <app-structure-options-modal - [userProfile]="userProfile" - (closed)="ngOnInit()" - ></app-structure-options-modal> - </div> - </div> +<div + fxLayout="column" + fxLayoutAlign="start center" + fxLayoutGap="16px" + class="content-container full-screen" + *ngIf="userProfile" +> + <section> + <div fxLayout="row" fxLayoutAlign="start center" fxFill> + <h1>Profil</h1> + <app-button + class="hide-on-mobile" + [type]="'button'" + [iconBtn]="'edit'" + [text]="'Modifier mon profil'" + [style]="buttonTypeEnum.SecondaryWide" + routerLink="/profile/edit" + [routerLinkActive]="'active'" + ></app-button> + <app-button + class="hide-on-desktop" + [type]="'button'" + [iconBtn]="'edit'" + [style]="buttonTypeEnum.SecondaryOnlyIcon" + routerLink="/profile/edit" + [routerLinkActive]="'active'" + ></app-button> + </div> + <div class="profile" fxLayout="row"> + <app-svg-icon + class="avatar" + (click)="isLoggedIn ? openProfileMenu() : goToLoginPage()" + [type]="'avatar'" + [icon]="'defaultAvatar'" + [iconClass]="'icon-112'" + ></app-svg-icon> + <div class="information"> + <div class="name">{{ userProfile.name }} {{ userProfile.surname }}</div> + <div class="job">{{ userProfile.job.name }}, {{ userProfile.employer.name }}</div> + <div class="phone">{{ userProfile.phone | phone }}</div> + <a class="email" href="mailto:{{ userProfile.email }}">{{ userProfile.email }}</a> + <div class="description" *ngIf="userProfile.description">{{ userProfile.description }}</div> </div> </div> - </div> - <div class="structureSection"> - <div class="section-container" fxLayoutGap="18px" fxLayout="column"> - <ng-container *ngIf="userProfile.structuresLink.length > 0 && structures"> - <div class="structureCard" *ngFor="let s of structures; let i = index"> - <div class="structureInfo" fxLayout="column" fxLayoutGap="14px"> - <div fxLayout="row" fxLayoutAlign="space-between start" fxLayoutGap="20px"> - <a class="structureName" routerLink="/acteurs" [state]="{ data: s.structure }">{{ - s.structure.structureName - }}</a> - <app-structure-options-modal - [structure]="s" - (closed)="ngOnInit()" - (closedWithRefresh)="ngOnInit()" - ></app-structure-options-modal> - </div> - <div fxLayout="column" fxLayoutGap="14px" class="ownersBlock"> - <p class="ownerName" *ngFor="let owner of s.owners">{{ owner.email }}</p> + <div class="call-to-action" *ngIf="!userProfile.description" fxLayoutAlign="center center" fxFill> + <app-button + [type]="'button'" + [iconBtn]="'edit'" + [text]="'Ajouter une description'" + [style]="buttonTypeEnum.SecondaryUltraWide" + routerLink="/profile/edit" + [routerLinkActive]="'active'" + ></app-button> + </div> + </section> + + <section> + <div fxLayout="row" fxLayoutAlign="start center" fxFill> + <h1>Structures</h1> + <app-button + class="hide-on-mobile" + [type]="'button'" + [iconBtn]="'edit'" + [text]="'Gérer mes structures'" + [style]="buttonTypeEnum.SecondaryWide" + routerLink="/profile/edit" + [routerLinkActive]="'active'" + ></app-button> + <app-button + class="hide-on-desktop" + [type]="'button'" + [iconBtn]="'edit'" + [style]="buttonTypeEnum.SecondaryOnlyIcon" + routerLink="/profile/edit" + [routerLinkActive]="'active'" + ></app-button> + </div> + <div fxLayout="column"> + <div class="section-container" fxLayoutGap="18px" fxLayout="column"> + <ng-container *ngIf="userProfile.structuresLink.length > 0 && structures"> + <div class="structureCard" *ngFor="let s of structures; let i = index"> + <div class="structureInfo" fxLayout="column" fxLayoutGap="14px"> + <div fxLayout="row" fxLayoutAlign="space-between start" fxLayoutGap="20px"> + <a class="structureName" routerLink="/acteurs" [state]="{ data: s.structure }">{{ + s.structure.structureName + }}</a> + <app-structure-options-modal + [structure]="s" + (closed)="ngOnInit()" + (closedWithRefresh)="ngOnInit()" + ></app-structure-options-modal> + </div> + <div fxLayout="column" fxLayoutGap="14px" class="ownersBlock"> + <p class="ownerName" *ngFor="let owner of s.owners">{{ owner.email }}</p> + </div> </div> </div> - </div> - </ng-container> - - <div class="addSection" fxLayout="row" fxLayoutAlign="center center"> - <app-button - class="hide-on-print" - [type]="'button'" - [text]="'Ajouter une structure'" - [iconBtn]="'add'" - (action)="addStructure()" - ></app-button> + </ng-container> </div> </div> - </div> + </section> + + <section> + <div fxLayout="row" fxLayoutAlign="start center" fxFill> + <h1>Ressources</h1> + </div> + </section> + <section> + <div fxLayout="row" fxLayoutAlign="start center" fxFill> + <h1>Newsletter</h1> + </div> + </section> </div> diff --git a/src/app/profile/profile.component.scss b/src/app/profile/profile.component.scss index 1323a009e5038e508d6f8e9744b8d89b667b3413..edb87e36fa787f5ac2814c040eee88523a483313 100644 --- a/src/app/profile/profile.component.scss +++ b/src/app/profile/profile.component.scss @@ -3,61 +3,72 @@ @import '../../assets/scss/hyperlink'; @import '../../assets/scss/shapes'; @import '../../assets/scss/breakpoint'; +@import '../../assets/scss/layout'; .content-container { - padding-top: 0; + padding-block: 16px; + padding-inline: 4px; } -.section-container { - width: 50%; - padding: 20px 0; - .profileInformation { +section { + width: $content-desktop-width; + display: flex; + flex-direction: column; + align-items: flex-start; + box-sizing: border-box; + padding: 24px 40px 40px; + background: $white; + border: 1px solid $grey-6; + border-radius: 8px; + @media #{$tablet} { width: 100%; + padding: 24px 24px 40px 24px; } - .cameraProfile { - width: 128px; - margin-right: 16px; - height: 113px; - } - @media #{$tablet} { - .cameraProfile { - display: none; - } - width: 70%; + + h1 { + @include lato-regular-24; + color: $grey-1; + flex-grow: 1; } - @media #{$large-phone} { - width: 90%; + + .call-to-action { + margin-top: 40px; } } -.profileSection { - background: $white; - border-bottom: 1px solid $grey-4; - .profileName { - @include lato-bold-26; - margin: 0 !important; + +.profile { + @include lato-regular-15; + .avatar { + height: 112px; + width: 112px; + background: $grey-8; + border-radius: 8px; + margin-right: 16px; + @media #{$tablet} { + display: none; + } } - .profileEmail { - span { - @include lato-regular-16; - color: $grey-2; + .information { + .name { + @include lato-bold-18; + min-height: 24px; } - p { - margin: 0; - @include lato-regular-18; + .job { + min-height: 24px; + margin-bottom: 8px; + } + .phone { + min-height: 24px; + } + .email { + display: block; + @include lato-regular-14; + height: 24px; + text-decoration: underline; + color: $grey-1; + margin-bottom: 8px; + } + .description { + max-width: 600px; } - } -} -.structureSection { - margin-bottom: 108px; - .ownersBlock { - width: 100%; - max-height: 300px; - overflow: auto; - } -} - -.addSection { - button { - background: $red; - color: $white; } } diff --git a/src/app/profile/profile.component.ts b/src/app/profile/profile.component.ts index 49d2d5cbeed861145f5267e587352e01f0756af6..2a2810b384606d33dfcb531d353164ee438c53d9 100644 --- a/src/app/profile/profile.component.ts +++ b/src/app/profile/profile.component.ts @@ -2,8 +2,8 @@ import { Component, OnInit } from '@angular/core'; import { Router } from '@angular/router'; import { StructureWithOwners } from '../models/structureWithOwners.model'; import { User } from '../models/user.model'; -import { AuthService } from '../services/auth.service'; import { StructureService } from '../services/structure.service'; +import { ButtonType } from '../shared/components/button/buttonType.enum'; import { ProfileService } from './services/profile.service'; @Component({ @@ -14,16 +14,16 @@ import { ProfileService } from './services/profile.service'; export class ProfileComponent implements OnInit { public userProfile: User; public structures: StructureWithOwners[] = []; + public buttonTypeEnum = ButtonType; constructor( private profileService: ProfileService, private structureService: StructureService, - private router: Router, - private authService: AuthService + private router: Router ) {} ngOnInit(): void { - this.profileService.getProfile().then((profile) => { + this.profileService.getProfile().then((profile: User) => { this.userProfile = profile; this.structures = []; profile.structuresLink.forEach((structureId) => { @@ -37,8 +37,4 @@ export class ProfileComponent implements OnInit { public addStructure(): void { this.router.navigateByUrl('/form/structure'); } - - public logout(): void { - this.authService.logout(); - } } diff --git a/src/app/services/auth.service.ts b/src/app/services/auth.service.ts index 3b19a0dbdc8521e89b625bf0ace2bdef3695589d..4726f207033f4dc9406e2146fdf2e784a6a81958 100644 --- a/src/app/services/auth.service.ts +++ b/src/app/services/auth.service.ts @@ -1,94 +1,98 @@ -import { HttpClient } from '@angular/common/http'; -import { Injectable } from '@angular/core'; -import { Router } from '@angular/router'; -import { DateTime } from 'luxon'; -import { BehaviorSubject, Observable } from 'rxjs'; -import { map } from 'rxjs/operators'; -import { UserAuth } from '../models/user-auth.model'; -import { User } from '../models/user.model'; -@Injectable({ - providedIn: 'root', -}) -export class AuthService { - private userSubject: BehaviorSubject<UserAuth>; - public user: Observable<UserAuth>; - - constructor(private http: HttpClient, private router: Router) { - this.userSubject = new BehaviorSubject<UserAuth>(JSON.parse(localStorage.getItem('user'))); - this.user = this.userSubject.asObservable(); - } - - public get userValue(): UserAuth { - return this.userSubject.value; - } - - public get token(): string { - if (this.userSubject.value) { - return this.userSubject.value.accessToken; - } - return null; - } - - public logout(): void { - localStorage.removeItem('user'); - this.userSubject.next(null); - window.location.replace('/home'); - } - - public isLoggedIn(): boolean { - if (this.userValue) { - return new DateTime.local().setZone('Europe/Paris') < this.getExpiration(); - } - return false; - } - - public getUsernameDisplay(): string { - return `${this.userValue.name}`; - } - - public getUsersurnameDisplay(): string { - return `${this.userValue.surname}`; - } - - public getUserEmailDisplay(): string { - return `${this.userValue.username}`; - } - - private getExpiration(): DateTime { - return DateTime.fromISO(this.userValue.expiresAt, { zone: 'Europe/Paris' }); - } - - public register(user: User): Observable<any> { - return this.http.post('api/users', user); - } - - public login(email: string, password: string): Observable<any> { - return this.http - .post<UserAuth>('api/auth/login', { email, password }) - .pipe( - map((user) => { - // store user details and jwt token in local storage to keep user logged in between page refreshes - localStorage.setItem('user', JSON.stringify(user)); - this.userSubject.next(user); - return user; - }) - ); - } - - public verifyUser(userId: string, token: string): Observable<any> { - return this.http.post(`api/users/verify/${userId}`, null, { - params: { token }, - }); - } - - public resetPassword(email: string): Observable<any> { - return this.http.post(`api/users/reset-password`, { email }); - } - - public resetPasswordApply(token: string, password: string): Observable<any> { - return this.http.post(`api/users/reset-password/apply`, { - token, - password, - }); - } -} +import { HttpClient } from '@angular/common/http'; +import { Injectable } from '@angular/core'; +import { Router } from '@angular/router'; +import { DateTime } from 'luxon'; +import { BehaviorSubject, Observable } from 'rxjs'; +import { map } from 'rxjs/operators'; +import { UserAuth } from '../models/user-auth.model'; +import { User } from '../models/user.model'; +@Injectable({ + providedIn: 'root', +}) +export class AuthService { + private userSubject: BehaviorSubject<UserAuth>; + public user: Observable<UserAuth>; + + constructor(private http: HttpClient, private router: Router) { + this.userSubject = new BehaviorSubject<UserAuth>(JSON.parse(localStorage.getItem('user'))); + this.user = this.userSubject.asObservable(); + } + + public get userValue(): UserAuth { + return this.userSubject.value; + } + + public get token(): string { + if (this.userSubject.value) { + return this.userSubject.value.accessToken; + } + return null; + } + + public logout(): void { + localStorage.removeItem('user'); + this.userSubject.next(null); + window.location.replace('/home'); + } + + public isLoggedIn(): boolean { + if (this.userValue) { + return new DateTime.local().setZone('Europe/Paris') < this.getExpiration(); + } + return false; + } + + public getUsernameDisplay(): string { + return `${this.userValue.name}`; + } + + public getUsersurnameDisplay(): string { + return `${this.userValue.surname}`; + } + + public getUserfullnameDisplay(): string { + return `${this.userValue.surname} ${this.userValue.name}`; + } + + public getUserEmailDisplay(): string { + return `${this.userValue.username}`; + } + + private getExpiration(): DateTime { + return DateTime.fromISO(this.userValue.expiresAt, { zone: 'Europe/Paris' }); + } + + public register(user: User): Observable<any> { + return this.http.post('api/users', user); + } + + public login(email: string, password: string): Observable<any> { + return this.http + .post<UserAuth>('api/auth/login', { email, password }) + .pipe( + map((user) => { + // store user details and jwt token in local storage to keep user logged in between page refreshes + localStorage.setItem('user', JSON.stringify(user)); + this.userSubject.next(user); + return user; + }) + ); + } + + public verifyUser(userId: string, token: string): Observable<any> { + return this.http.post(`api/users/verify/${userId}`, null, { + params: { token }, + }); + } + + public resetPassword(email: string): Observable<any> { + return this.http.post(`api/users/reset-password`, { email }); + } + + public resetPasswordApply(token: string, password: string): Observable<any> { + return this.http.post(`api/users/reset-password/apply`, { + token, + password, + }); + } +} diff --git a/src/app/shared/components/button/button.component.html b/src/app/shared/components/button/button.component.html index aef5c8276777fe84faf29ca653c304133e1e9ec8..e8495712311bdc7cdf0bd14feaadcdd8db6c65a5 100644 --- a/src/app/shared/components/button/button.component.html +++ b/src/app/shared/components/button/button.component.html @@ -94,8 +94,16 @@ </button> </ng-container> -<ng-container *ngIf="style === buttonTypeEnum.Secondary"> - <button class="btn-regular secondary" type="{{ type }}" (click)="doAction()" [disabled]="disabled"> +<ng-container *ngIf="style === buttonTypeEnum.Secondary || style === buttonTypeEnum.SecondaryWide"> + <button + [ngClass]="{ + 'btn-regular secondary': true, + wide: style === buttonTypeEnum.SecondaryWide + }" + type="{{ type }}" + (click)="doAction()" + [disabled]="disabled" + > <div *ngIf="!iconBtn" [ngClass]="extraClass" class="text">{{ text }}</div> <div *ngIf="iconBtn && iconPos === 'left'" @@ -118,6 +126,38 @@ </button> </ng-container> +<ng-container *ngIf="style === buttonTypeEnum.SecondaryUltraWide"> + <button class="btn-regular secondary ultrawide" type="{{ type }}" (click)="doAction()" [disabled]="disabled"> + <div *ngIf="!iconBtn" [ngClass]="extraClass" class="text">{{ text }}</div> + <div + *ngIf="iconBtn && iconPos === 'left'" + fxLayout="row center" + class="text withIcon left" + fxLayoutAlign="center center" + > + <app-svg-icon [type]="iconType" [icon]="iconBtn" [iconColor]="'currentColor'"></app-svg-icon> + <span>{{ text }}</span> + </div> + <div + *ngIf="iconBtn && iconPos === 'right'" + fxLayout="row center" + class="text withIcon right" + fxLayoutAlign="center center" + > + <span>{{ text }}</span> + <app-svg-icon [type]="iconType" [icon]="iconBtn" [iconColor]="'currentColor'"></app-svg-icon> + </div> + </button> +</ng-container> + +<ng-container *ngIf="style === buttonTypeEnum.SecondaryOnlyIcon"> + <button class="btn-regular secondary" type="{{ type }}" (click)="doAction()" [disabled]="disabled"> + <div *ngIf="iconBtn" fxLayout="row center" class="text withIcon center" fxLayoutAlign="space-around center"> + <app-svg-icon [type]="iconType" [icon]="iconBtn" [iconColor]="'currentColor'"></app-svg-icon> + </div> + </button> +</ng-container> + <ng-container *ngIf="style === buttonTypeEnum.ButtonPhone"> <button [disabled]="disabled" class="btn-switch-phone" type="{{ type }}" (click)="doAction()"> <div *ngIf="!iconBtn" class="text">{{ text }}</div> diff --git a/src/app/shared/components/button/button.component.scss b/src/app/shared/components/button/button.component.scss index 1ba789e55da9632cf7f36ed7277d9e62674b2eef..c198e1f0f5413c2fa99e4a16716c6e63614ec9a7 100644 --- a/src/app/shared/components/button/button.component.scss +++ b/src/app/shared/components/button/button.component.scss @@ -1,5 +1,6 @@ @import '../../../../assets/scss/typography'; @import '../../../../assets/scss/color'; +@import '../../../../assets/scss/breakpoint'; @import '../../../../assets/scss/shapes'; @mixin btn-bold { @@ -116,7 +117,6 @@ button { border: 1px solid $grey-1; color: $grey-1; font-size: 14px; - // @include btn-regular; &.withIcon { color: $grey-1; } @@ -124,6 +124,24 @@ button { .small-text { height: 22px !important; } + &.wide { + div:first-child { + min-width: 50px; + width: 184px; + } + } + &.ultrawide { + div:first-child { + min-width: 50px; + width: 400px; + @media #{$tablet} { + width: 200px; + } + span { + padding-inline: 4px; + } + } + } } &.tertiary { div:first-child { @@ -199,6 +217,10 @@ button { &.right { padding-right: 8px !important; } + &.center { + padding-left: 6px !important; + padding-right: 6px !important; + } } } } diff --git a/src/app/shared/components/button/buttonType.enum.ts b/src/app/shared/components/button/buttonType.enum.ts index 93b2cba4521111da47af7b23219e54dadacbd9d6..99cd644b020d619d231a142fa18c2f6e3d269344 100644 --- a/src/app/shared/components/button/buttonType.enum.ts +++ b/src/app/shared/components/button/buttonType.enum.ts @@ -2,6 +2,9 @@ export enum ButtonType { Regular, Primary, Secondary, + SecondaryWide, + SecondaryUltraWide, + SecondaryOnlyIcon, Tertiary, ButtonPhone, Filter, diff --git a/src/app/shared/components/svg-icon/svg-icon.component.scss b/src/app/shared/components/svg-icon/svg-icon.component.scss index 72addb32cb6f82c4ba6c330a3780c023fe438b03..96eb211f89649753ba21e0c836aae4e67a906379 100644 --- a/src/app/shared/components/svg-icon/svg-icon.component.scss +++ b/src/app/shared/components/svg-icon/svg-icon.component.scss @@ -33,6 +33,10 @@ width: 32px; height: 32px; } + &.icon-40 { + width: 40px; + height: 40px; + } &.icon-75 { width: 4.688em; } @@ -40,6 +44,10 @@ height: 80px; width: 80px; } + &.icon-112 { + height: 112px; + width: 112px; + } &.validation { height: 36px; width: 20px; diff --git a/src/assets/avatar/sprite.svg b/src/assets/avatar/sprite.svg new file mode 100644 index 0000000000000000000000000000000000000000..422f307d3f21dcef0d03d9773a0dc0da8ebc4758 --- /dev/null +++ b/src/assets/avatar/sprite.svg @@ -0,0 +1,23 @@ +<svg xmlns="http://www.w3.org/2000/svg"> + + <symbol id="defaultAvatar" viewBox="0 0 40 40" fill="none" xmlns="http://www.w3.org/2000/svg"> + <circle r="3.33333" transform="matrix(-1 0 0 1 10.0002 24.6673)" fill="#B1B0B0"/> + <circle r="3.33333" transform="matrix(-1 0 0 1 30.0002 24.6673)" fill="#B1B0B0"/> + <rect width="8" height="12" rx="4" transform="matrix(-1 0 0 1 24 28.0225)" fill="#D4D4D4"/> + <rect width="8" height="12" rx="4" transform="matrix(-1 0 0 1 24 25.334)" fill="#B1B0B0"/> + <circle r="10.6667" transform="matrix(-1 0 0 1 20.0003 24.0007)" fill="#D4D4D4"/> + <path d="M22.4594 28.1165C21.1149 29.7394 18.8854 29.7394 17.5409 28.1165C17.0912 27.5737 17.4209 26.667 18.0679 26.667L21.9324 26.667C22.5794 26.667 22.9091 27.5737 22.4594 28.1165Z" fill="white"/> + <circle cx="28.0003" cy="26.6673" r="1.33333" fill="#DA3635"/> + <circle cx="12.0003" cy="26.6673" r="1.33333" fill="#DA3635"/> + <path d="M27.0007 20.001H22.6148C21.9642 20.001 21.4868 20.6124 21.6446 21.2435L22.1002 23.0657C22.4335 24.399 23.6314 25.3343 25.0057 25.3343C26.6598 25.3343 28.0007 23.9934 28.0007 22.3394V21.001C28.0007 20.4487 27.5529 20.001 27.0007 20.001Z" fill="black"/> + <path d="M13.0003 20.001H17.3862C18.0368 20.001 18.5141 20.6124 18.3564 21.2435L17.9008 23.0657C17.5675 24.399 16.3696 25.3343 14.9953 25.3343C13.3412 25.3343 12.0003 23.9934 12.0003 22.3394V21.001C12.0003 20.4487 12.448 20.001 13.0003 20.001Z" fill="black"/> + <path d="M17.3335 21.3343L19.1057 20.4482C19.6688 20.1667 20.3315 20.1667 20.8946 20.4482L22.6668 21.3343" stroke="black" stroke-width="1.33333"/> + <line x1="0.666667" y1="-0.666667" x2="3.30647" y2="-0.666667" transform="matrix(0.942009 -0.335587 0.409703 0.912219 13.334 21.334)" stroke="black" stroke-width="1.33333" stroke-linecap="round"/> + <line x1="0.666667" y1="-0.666667" x2="3.30647" y2="-0.666667" transform="matrix(-0.942009 -0.335587 -0.409703 0.912219 26.6675 21.334)" stroke="black" stroke-width="1.33333" stroke-linecap="round"/> + <path d="M15.6069 14.863C15.6069 18.7159 18.9168 17.3337 22.9998 17.3337C24.7581 17.3337 26.286 18.5419 26.6912 20.2529L30.2094 35.1075C30.4575 36.1553 31.9998 35.9752 31.9998 34.8984V19.2788C31.9998 15.8305 30.2574 12.6156 27.3683 10.7331C24.5175 8.87561 20.8129 7.43023 18.1739 9.57797C16.602 10.8573 15.6069 12.7505 15.6069 14.863Z" fill="black"/> + <path fill-rule="evenodd" clip-rule="evenodd" d="M29.3333 29.1677C28.1546 31.2919 26.2718 32.9714 24 33.891V35.9997C24 37.3808 23.3 38.5984 22.2355 39.3172C22.9436 39.7502 23.776 39.9997 24.6667 39.9997C27.244 39.9997 29.3333 37.9103 29.3333 35.333V29.1677ZM29.959 28.1375C29.8479 28.3034 29.7328 28.4663 29.6138 28.6262C29.6919 28.4643 29.766 28.3001 29.836 28.1338C29.8768 28.1358 29.9178 28.137 29.959 28.1375ZM31.979 22.1219C31.9929 21.8837 32 21.6438 32 21.4021C32 14.7365 26.6274 9.33301 20 9.33301C13.3726 9.33301 8 14.7365 8 21.4021C8 21.6438 8.00706 21.8837 8.02099 22.1219C8.48102 21.7819 9.03162 21.5575 9.62993 21.4914C10.7581 16.8107 14.9729 13.3331 20 13.3331C25.0271 13.3331 29.2419 16.8107 30.3701 21.4914C30.9684 21.5575 31.519 21.7819 31.979 22.1219ZM10.041 28.1375C10.0822 28.137 10.1232 28.1358 10.164 28.1338C10.234 28.3001 10.3081 28.4643 10.3862 28.6262C10.2672 28.4663 10.1521 28.3034 10.041 28.1375Z" fill="black"/> + <path fill-rule="evenodd" clip-rule="evenodd" d="M19.9993 25.6367C19.6823 25.3202 19.1766 25.2358 18.7704 25.4604L16.4965 26.7179C16.017 26.9831 15.8576 27.5784 16.1404 28.0477C16.4233 28.5169 17.0413 28.6824 17.5207 28.4172L19.7946 27.1597C19.8712 27.1174 19.9396 27.0666 19.9993 27.0093C20.0591 27.0666 20.1275 27.1174 20.2041 27.1597L22.478 28.4172C22.9574 28.6824 23.5754 28.5169 23.8582 28.0477C24.1411 27.5784 23.9817 26.9831 23.5022 26.7179L21.2283 25.4604C20.8221 25.2357 20.3164 25.3202 19.9993 25.6367Z" fill="black"/> + </symbol> + +</svg> + diff --git a/src/assets/ico/edit.svg b/src/assets/ico/edit.svg new file mode 100644 index 0000000000000000000000000000000000000000..6b69f1c4d47cd253100ef1c0398f24b379b9d7f7 --- /dev/null +++ b/src/assets/ico/edit.svg @@ -0,0 +1,12 @@ +<svg width="23" height="23" viewBox="0 0 23 23" fill="none" xmlns="http://www.w3.org/2000/svg"> +<g clip-path="url(#clip0_7929_46214)"> +<path d="M4.28317 15.287L3.65575 18.6944C3.65575 18.9504 3.85693 19.1516 4.11297 19.1516L7.52029 18.5242C7.63917 18.5242 7.75804 18.4784 7.84034 18.387L17.4626 8.77388L14.0335 5.34473L4.42034 14.9578C4.32889 15.0493 4.28317 15.159 4.28317 15.287Z" stroke="#333333" stroke-width="1.5"/> +<path d="M19.1358 7.09997C19.4924 6.74334 19.4924 6.16724 19.1358 5.81061L16.996 3.67082C16.8252 3.49959 16.5932 3.40336 16.3513 3.40336C16.1094 3.40336 15.8775 3.49959 15.7067 3.67082L14.0332 5.34424L17.4624 8.7734L19.1358 7.09997Z" stroke="#333333" stroke-width="1.5"/> +<path d="M4.78906 14.6543L8.15154 18.0168" stroke="#333333" stroke-width="1.5"/> +</g> +<defs> +<clipPath id="clip0_7929_46214"> +<rect width="22" height="22" fill="white" transform="translate(0.5 0.5)"/> +</clipPath> +</defs> +</svg> diff --git a/src/assets/ico/profile.svg b/src/assets/ico/profile.svg new file mode 100644 index 0000000000000000000000000000000000000000..9a0a48eaf97244c56079fcea5dfa560176ba858d --- /dev/null +++ b/src/assets/ico/profile.svg @@ -0,0 +1,4 @@ +<svg width="32" height="32" viewBox="0 0 32 32" fill="none" xmlns="http://www.w3.org/2000/svg"> +<path d="M22.7499 24.492V21.3125C22.7499 20.4008 22.3878 19.5265 21.7431 18.8818C21.0985 18.2372 20.2241 17.875 19.3124 17.875H12.4375C11.5258 17.875 10.6515 18.2372 10.0068 18.8818C9.36216 19.5265 9 20.4008 9 21.3125V24.492" stroke="#333333" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/> +<path d="M15.875 15.625C18.1877 15.625 20.0625 13.7502 20.0625 11.4375C20.0625 9.1248 18.1877 7.25 15.875 7.25C13.5623 7.25 11.6875 9.1248 11.6875 11.4375C11.6875 13.7502 13.5623 15.625 15.875 15.625Z" stroke="#333333" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/> +</svg> diff --git a/src/assets/ico/sprite.svg b/src/assets/ico/sprite.svg index 7bc59a3ee6a6708f0a31a4f1d112ced489c6728c..d327ba0062a2577bdb29b8838d2e775ef8fbc241 100644 --- a/src/assets/ico/sprite.svg +++ b/src/assets/ico/sprite.svg @@ -74,10 +74,6 @@ <path d="M6.97363 12.9062C6.34733 12.9062 5.89876 12.7877 5.62793 12.5507C5.36556 12.3053 5.23438 11.9879 5.23438 11.5986C5.23438 11.2092 5.36556 10.8961 5.62793 10.6591C5.89876 10.4137 6.34733 10.2952 6.97363 10.3036C14.44 10.3036 10.571 10.3036 17.0156 10.3036C17.6419 10.3036 18.0863 10.4221 18.3486 10.6591C18.6195 10.8961 18.7549 11.2092 18.7549 11.5986C18.7549 11.9879 18.6195 12.3053 18.3486 12.5507C18.0863 12.7877 17.6419 12.9062 17.0156 12.9062C9.63742 12.9062 13.3678 12.9062 6.97363 12.9062Z" stroke="none"/> </symbol> -<symbol id="edit" viewBox="0 0 22 22" xmlns="http://www.w3.org/2000/svg"> -<path fill-rule="evenodd" clip-rule="evenodd" d="M17.3745 2.62547C17.72 2.28003 18.3461 2.34613 18.7731 2.7731L20.3193 4.3193C20.7463 4.74627 20.8124 5.37243 20.4669 5.71787L18.6115 7.57331L15.5191 4.48091L17.3745 2.62547ZM17.993 8.1918L14.9006 5.0994L5.62344 14.3766L8.71584 17.469L17.993 8.1918ZM8.09736 18.0874L5.00496 14.995L4.74469 15.2553C4.60266 15.3973 4.52461 15.5949 4.52337 15.8155L3.77535 18.7134C3.7736 19.0246 4.0678 19.3188 4.37904 19.3171L7.27695 18.569C7.49751 18.5678 7.69506 18.4897 7.83709 18.3477L8.09736 18.0874Z" stroke="none"/> -</symbol> - <symbol id="liste" viewBox="0 0 32 32" xmlns="http://www.w3.org/2000/svg"> <circle cx="7.75" cy="10.75" r="0.75" fill="white"/> <rect x="10" y="10" width="15" height="1.5" rx="0.75" fill="white"/> @@ -262,9 +258,9 @@ </symbol> <symbol id="menu" viewBox="0 0 32 32" fill="none" xmlns="http://www.w3.org/2000/svg"> -<rect x="5" y="9" width="23" height="2" rx="1" fill="#333333"/> -<rect x="5" y="15" width="23" height="2" rx="1" fill="#333333"/> -<rect x="5" y="21" width="23" height="2" rx="1" fill="#333333"/> +<rect x="9" y="10" width="14" height="1.5" rx="0.75" fill="#333333"/> +<rect x="9" y="15.5" width="14" height="1.5" rx="0.75" fill="#333333"/> +<rect x="9" y="21" width="14" height="1.5" rx="0.75" fill="#333333"/> </symbol> <symbol id="news-location" viewBox="0 0 29 31" xmlns="http://www.w3.org/2000/svg"> @@ -428,4 +424,23 @@ <path d="M28.0279 27.0279C28.5486 27.5486 28.6541 28.2873 28.2636 28.6778C27.8731 29.0683 27.1344 28.9628 26.6137 28.4421L11.5287 13.3572C11.008 12.8365 10.9025 12.0978 11.293 11.7072C11.6836 11.3167 12.4223 11.4222 12.943 11.9429L28.0279 27.0279Z" fill="#333333"/> </symbol> +<symbol id="profile" viewBox="0 0 32 32" fill="none" xmlns="http://www.w3.org/2000/svg"> + <path d="M22.7499 24.492V21.3125C22.7499 20.4008 22.3878 19.5265 21.7431 18.8818C21.0985 18.2372 20.2241 17.875 19.3124 17.875H12.4375C11.5258 17.875 10.6515 18.2372 10.0068 18.8818C9.36216 19.5265 9 20.4008 9 21.3125V24.492" stroke="#333333" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/> + <path d="M15.875 15.625C18.1877 15.625 20.0625 13.7502 20.0625 11.4375C20.0625 9.1248 18.1877 7.25 15.875 7.25C13.5623 7.25 11.6875 9.1248 11.6875 11.4375C11.6875 13.7502 13.5623 15.625 15.875 15.625Z" stroke="#333333" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/> +</symbol> + +<symbol id="edit" viewBox="0 0 23 23" fill="none" xmlns="http://www.w3.org/2000/svg"> + <g clip-path="url(#clip0_7929_46214)"> + <path d="M4.28317 15.287L3.65575 18.6944C3.65575 18.9504 3.85693 19.1516 4.11297 19.1516L7.52029 18.5242C7.63917 18.5242 7.75804 18.4784 7.84034 18.387L17.4626 8.77388L14.0335 5.34473L4.42034 14.9578C4.32889 15.0493 4.28317 15.159 4.28317 15.287Z" stroke="#333333" stroke-width="1.5"/> + <path d="M19.1358 7.09997C19.4924 6.74334 19.4924 6.16724 19.1358 5.81061L16.996 3.67082C16.8252 3.49959 16.5932 3.40336 16.3513 3.40336C16.1094 3.40336 15.8775 3.49959 15.7067 3.67082L14.0332 5.34424L17.4624 8.7734L19.1358 7.09997Z" stroke="#333333" stroke-width="1.5"/> + <path d="M4.78906 14.6543L8.15154 18.0168" stroke="#333333" stroke-width="1.5"/> + </g> + <defs> + <clipPath id="clip0_7929_46214"> + <rect width="22" height="22" fill="white" transform="translate(0.5 0.5)"/> + </clipPath> + </defs> +</symbol> + + </svg> diff --git a/src/assets/scss/_color.scss b/src/assets/scss/_color.scss index a6a85037272c142e3ca42e14f94746693676bf6d..5aeb2b9a1ed69f92400de37154365a677f1af24b 100644 --- a/src/assets/scss/_color.scss +++ b/src/assets/scss/_color.scss @@ -33,7 +33,8 @@ $primary-color-light: #fef0f0; $default-link-color: $grey-2; $button-secondary: $red-dark; $app-background: $grey-8; -$modal-background: rgba($grey-1, 0.75); +$modal-background-transparent: rgba($grey-1, 0); +$modal-background: rgba($grey-1, 0.25); $app-background: $grey-8; $ram-hover-principal: $primary-color; $scrollbar-track: rgba($grey-8, 0.65); diff --git a/src/assets/scss/_layout.scss b/src/assets/scss/_layout.scss index d6b0a0c745cee97270a60f4c2f1f4de6fe5bcd03..7fa8f6f12468e287e59c81a27fc1901eac44287c 100644 --- a/src/assets/scss/_layout.scss +++ b/src/assets/scss/_layout.scss @@ -1,4 +1,4 @@ -$header-height: 70px; +$header-height: 55px; $footer-height: 56px; $footer-form-height: 36px; $header-height-phone: 70px; @@ -6,3 +6,4 @@ $footer-height-phone: 75px; $progressBar-height: 50px; $header-post-height: 180px; $header-post-height-mobile: 140px; +$content-desktop-width: 980px; diff --git a/src/assets/scss/_shapes.scss b/src/assets/scss/_shapes.scss index 37cbbcd9ce326e2f1e8aac16a774c6d497874521..e34528f91827599d42628026450616aa2df87686 100644 --- a/src/assets/scss/_shapes.scss +++ b/src/assets/scss/_shapes.scss @@ -8,6 +8,7 @@ $round-bouton-radius: 1.25em; $round-radius: 50%; $round-button: 40px; $simple-shadow: 0px 0px 4px rgba(0, 0, 0, 0.25); +$menu-shadow: 0px 0px 4px rgba(0, 0, 0, 0.25); $mat-tab-shadow: 0px 2px 7px rgba(0, 0, 0, 0.25); @mixin highlight { diff --git a/src/styles.scss b/src/styles.scss index a3794ee1e27c8eef11c6e0b0824c8456bfc58793..dbb591fe49746ead7841c8682ee72513473f91cf 100644 --- a/src/styles.scss +++ b/src/styles.scss @@ -377,6 +377,12 @@ button { box-shadow: 0 2px 1px rgba(0, 0, 0, 0.6); } +.hide-on-desktop { + display: none; + @media #{$tablet} { + display: block; + } +} .hide-on-mobile { @media #{$tablet} { display: none !important;