diff --git a/frontend/public/logo.png b/frontend/public/logo.png new file mode 100644 index 0000000000..ad331f7650 Binary files /dev/null and b/frontend/public/logo.png differ diff --git a/frontend/src/_services/authentication.service.ts b/frontend/src/_services/authentication.service.ts index eaaa17781a..a3c00626ba 100644 --- a/frontend/src/_services/authentication.service.ts +++ b/frontend/src/_services/authentication.service.ts @@ -21,7 +21,8 @@ export class AuthenticationService extends ApiService { ) { super(); const userData = localStorage.getItem('user'); - this.userSubject = new BehaviorSubject(userData ? JSON.parse(userData) : null); + const user: User | null = userData ? JSON.parse(userData) : null; + this.userSubject = new BehaviorSubject(user); this.user$ = this.userSubject.asObservable(); } @@ -29,8 +30,11 @@ export class AuthenticationService extends ApiService { return this.userSubject.value; } + public get isLoggedIn(): boolean { + return !!this.userSubject.value; + } + login(username: string, password: string) { - console.log('login', `${this.apiUrl}/auth/login`); return this.http .post( `${this.apiUrl}/auth/login`, diff --git a/frontend/src/app/account/layout.component.html b/frontend/src/app/account/layout.component.html index 21e10ec796..0036e0a2f0 100644 --- a/frontend/src/app/account/layout.component.html +++ b/frontend/src/app/account/layout.component.html @@ -1,4 +1,4 @@ -
+

Welcome to PeerPrep

diff --git a/frontend/src/app/account/login.component.ts b/frontend/src/app/account/login.component.ts index 4d01402374..8bc426b81c 100644 --- a/frontend/src/app/account/login.component.ts +++ b/frontend/src/app/account/login.component.ts @@ -13,7 +13,7 @@ import { AuthenticationService } from '../../_services/authentication.service'; selector: 'app-login', standalone: true, imports: [RouterLink, FormsModule, InputTextModule, ButtonModule, SelectButtonModule, PasswordModule, ToastModule], - providers: [MessageService, AuthenticationService], + providers: [MessageService], templateUrl: './login.component.html', styleUrl: './account.component.css', }) @@ -42,29 +42,26 @@ export class LoginComponent { this.isProcessingLogin = true; // authenticationService returns an observable that we can subscribe to - this.authenticationService - .login(this.userForm.username, this.userForm.password) - .pipe() - .subscribe({ - next: () => { - // get return url from route parameters or default to '/' - const returnUrl = this.route.snapshot.queryParams['returnUrl'] || '/'; - this.router.navigate([returnUrl]); - }, - error: error => { - this.isProcessingLogin = false; - const status = error.cause.status; - let errorMessage = 'An unknown error occurred'; - if (status === 400) { - errorMessage = 'Missing Fields'; - } else if (status === 401) { - errorMessage = 'Invalid username or password'; - } else if (status === 500) { - errorMessage = 'Internal Server Error'; - } - this.messageService.add({ severity: 'error', summary: 'Log In Error', detail: errorMessage }); - }, - }); + this.authenticationService.login(this.userForm.username, this.userForm.password).subscribe({ + next: () => { + // get return url from route parameters or default to '/' + const returnUrl = this.route.snapshot.queryParams['returnUrl'] || '/'; + this.router.navigate([returnUrl]); + }, + error: error => { + this.isProcessingLogin = false; + const status = error.cause.status; + let errorMessage = 'An unknown error occurred'; + if (status === 400) { + errorMessage = 'Missing Fields'; + } else if (status === 401) { + errorMessage = 'Invalid username or password'; + } else if (status === 500) { + errorMessage = 'Internal Server Error'; + } + this.messageService.add({ severity: 'error', summary: 'Log In Error', detail: errorMessage }); + }, + }); } else { console.log('Invalid form'); } diff --git a/frontend/src/app/account/register.component.ts b/frontend/src/app/account/register.component.ts index 263733c62f..ee67c228e4 100644 --- a/frontend/src/app/account/register.component.ts +++ b/frontend/src/app/account/register.component.ts @@ -28,7 +28,7 @@ import { AuthenticationService } from '../../_services/authentication.service'; ToastModule, ReactiveFormsModule, ], - providers: [MessageService, AuthenticationService], + providers: [MessageService], templateUrl: './register.component.html', styleUrl: './account.component.css', }) diff --git a/frontend/src/app/app.component.html b/frontend/src/app/app.component.html index 67e7bd4cd6..7088928e9a 100644 --- a/frontend/src/app/app.component.html +++ b/frontend/src/app/app.component.html @@ -1 +1,3 @@ + + diff --git a/frontend/src/app/app.component.ts b/frontend/src/app/app.component.ts index 8c4d3178de..d0722c2b03 100644 --- a/frontend/src/app/app.component.ts +++ b/frontend/src/app/app.component.ts @@ -2,11 +2,12 @@ import { Component } from '@angular/core'; import { RouterOutlet } from '@angular/router'; import { ButtonModule } from 'primeng/button'; import { PasswordModule } from 'primeng/password'; +import { NavigationBarComponent } from './navigation-bar/navigation-bar.component'; @Component({ selector: 'app-root', standalone: true, - imports: [RouterOutlet, ButtonModule, PasswordModule], + imports: [NavigationBarComponent, RouterOutlet, ButtonModule, PasswordModule], templateUrl: './app.component.html', styleUrl: './app.component.css', }) diff --git a/frontend/src/app/navigation-bar/navigation-bar.component.css b/frontend/src/app/navigation-bar/navigation-bar.component.css new file mode 100644 index 0000000000..2963b1ba03 --- /dev/null +++ b/frontend/src/app/navigation-bar/navigation-bar.component.css @@ -0,0 +1,46 @@ +::ng-deep .p-menubar { + border-radius: 0; + border-style: none; + position: sticky; + background-color: rgba(9, 9, 11, 0.8); + box-shadow: 0 2px 4px rgba(0, 0, 0, 0.5); + top: 0; + z-index: 100; + backdrop-filter: blur(10px); + justify-content: center; + height: 80px; +} + +:host ::ng-deep p-menubarsub { + width: 100%; +} + +::ng-deep .p-submenu-list { + right: 0%; + left: unset; +} + +a.fill-div { + display: block; + height: 100%; + width: 100%; + text-decoration: none; +} + +/* fonts */ +.poppins-thin { + font-family: "Poppins", sans-serif; + font-weight: 100; + font-style: normal; + } + + .poppins-bold { + font-family: "Poppins", sans-serif; + font-weight: 700; + font-style: normal; + } + +p.logo-font-size { + font-size: 28px; +} + \ No newline at end of file diff --git a/frontend/src/app/navigation-bar/navigation-bar.component.html b/frontend/src/app/navigation-bar/navigation-bar.component.html new file mode 100644 index 0000000000..f94925b7e3 --- /dev/null +++ b/frontend/src/app/navigation-bar/navigation-bar.component.html @@ -0,0 +1,18 @@ + + + + + + +
+ logo +

PeerPrep

+
+
+ + + + {{ item.label }} + + +
diff --git a/frontend/src/app/navigation-bar/navigation-bar.component.spec.ts b/frontend/src/app/navigation-bar/navigation-bar.component.spec.ts new file mode 100644 index 0000000000..95ea1d87d4 --- /dev/null +++ b/frontend/src/app/navigation-bar/navigation-bar.component.spec.ts @@ -0,0 +1,22 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; + +import { NavigationBarComponent } from './navigation-bar.component'; + +describe('NavigationBarComponent', () => { + let component: NavigationBarComponent; + let fixture: ComponentFixture; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + imports: [NavigationBarComponent], + }).compileComponents(); + + fixture = TestBed.createComponent(NavigationBarComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/frontend/src/app/navigation-bar/navigation-bar.component.ts b/frontend/src/app/navigation-bar/navigation-bar.component.ts new file mode 100644 index 0000000000..8857a2bbcb --- /dev/null +++ b/frontend/src/app/navigation-bar/navigation-bar.component.ts @@ -0,0 +1,89 @@ +import { Component, OnInit } from '@angular/core'; +import { Router } from '@angular/router'; +import { MenuItem } from 'primeng/api'; +import { CommonModule, NgFor } from '@angular/common'; +import { MenubarModule } from 'primeng/menubar'; +import { AuthenticationService } from '../../_services/authentication.service'; +import { User } from '../../_models/user.model'; + +@Component({ + selector: 'app-navigation-bar', + standalone: true, + imports: [MenubarModule, CommonModule, NgFor], + templateUrl: './navigation-bar.component.html', + styleUrl: './navigation-bar.component.css', +}) +export class NavigationBarComponent implements OnInit { + items: MenuItem[] | undefined; + + constructor( + private router: Router, + private authService: AuthenticationService, + ) {} + + ngOnInit() { + this.setMenuItems(); + this.authService.user$.subscribe(() => this.setMenuItems()); + } + + setMenuItems() { + if (this.authService.isLoggedIn) { + const user = this.authService.userValue as User; + this.items = [ + { + label: user.username, + icon: 'pi pi-user', + // route: '/profile', + style: { 'margin-left': 'auto' }, + items: [ + { + label: 'Find Match', + icon: 'pi pi-users', + // route: '', + class: 'p-submenu-list', + }, + { + label: 'View Questions', + icon: 'pi pi-file', + route: '/questions', + class: 'p-submenu-list', + }, + { + label: 'View Profile', + icon: 'pi pi-user', + // route: '', + class: 'p-submenu-list', + }, + { + label: 'View Match History', + icon: 'pi pi-trophy', + // route: '', + class: 'p-submenu-list', + }, + { + label: 'Logout', + icon: 'pi pi-sign-out', + class: 'p-submenu-list', + command: () => this.authService.logout(), + }, + ], + }, + ]; + } else { + this.items = [ + { + label: 'Login', + icon: 'pi pi-sign-in', + route: 'account/login', + style: { 'margin-left': 'auto' }, + }, + { + label: 'Sign Up', + icon: 'pi pi-pen-to-square', + route: '/account/register', + class: 'border', + }, + ]; + } + } +} diff --git a/frontend/src/app/questions/questions.component.html b/frontend/src/app/questions/questions.component.html index d87a8e9da4..7789d167ce 100644 --- a/frontend/src/app/questions/questions.component.html +++ b/frontend/src/app/questions/questions.component.html @@ -7,7 +7,7 @@ animationDuration=".5s" /> - +