From 7c58f493d5b90fddd31c954af693bde92ade0a48 Mon Sep 17 00:00:00 2001
From: Raj Jitendra Shah <141725750+Rajshah1302@users.noreply.github.com>
Date: Sun, 15 Dec 2024 20:50:27 +0530
Subject: [PATCH 1/3] Frontend Update
---
webiu-ui/src/app/app.config.ts | 3 +-
webiu-ui/src/app/app.routes.ts | 9 +-
.../components/navbar/navbar.component.html | 27 ++--
.../app/components/navbar/navbar.component.ts | 15 ++-
.../src/app/page/login/login.component.html | 57 +++++++++
.../src/app/page/login/login.component.scss | 110 +++++++++++++++++
.../app/page/login/login.component.spec.ts | 39 ++++++
.../src/app/page/login/login.component.ts | 115 ++++++++++++++++++
.../app/page/register/register.component.html | 61 ++++++++++
.../app/page/register/register.component.scss | 110 +++++++++++++++++
.../page/register/register.component.spec.ts | 0
.../app/page/register/register.component.ts | 53 ++++++++
webiu-ui/src/app/services/auth.service.ts | 60 +++++++++
webiu-ui/src/assets/login.svg | 14 +++
webiu-ui/src/index.html | 7 +-
webiu-ui/src/styles.scss | 3 +
16 files changed, 666 insertions(+), 17 deletions(-)
create mode 100644 webiu-ui/src/app/page/login/login.component.html
create mode 100644 webiu-ui/src/app/page/login/login.component.scss
create mode 100644 webiu-ui/src/app/page/login/login.component.spec.ts
create mode 100644 webiu-ui/src/app/page/login/login.component.ts
create mode 100644 webiu-ui/src/app/page/register/register.component.html
create mode 100644 webiu-ui/src/app/page/register/register.component.scss
create mode 100644 webiu-ui/src/app/page/register/register.component.spec.ts
create mode 100644 webiu-ui/src/app/page/register/register.component.ts
create mode 100644 webiu-ui/src/app/services/auth.service.ts
create mode 100644 webiu-ui/src/assets/login.svg
diff --git a/webiu-ui/src/app/app.config.ts b/webiu-ui/src/app/app.config.ts
index 6c6ef603..d3358e9c 100644
--- a/webiu-ui/src/app/app.config.ts
+++ b/webiu-ui/src/app/app.config.ts
@@ -2,7 +2,8 @@ import { ApplicationConfig } from '@angular/core';
import { provideRouter } from '@angular/router';
import { routes } from './app.routes';
+import { provideAnimationsAsync } from '@angular/platform-browser/animations/async';
export const appConfig: ApplicationConfig = {
- providers: [provideRouter(routes)]
+ providers: [provideRouter(routes), provideAnimationsAsync()]
};
diff --git a/webiu-ui/src/app/app.routes.ts b/webiu-ui/src/app/app.routes.ts
index 1954a9c8..1d0d88b3 100644
--- a/webiu-ui/src/app/app.routes.ts
+++ b/webiu-ui/src/app/app.routes.ts
@@ -7,11 +7,13 @@ import { CommunityComponent } from './page/community/community.component';
import { GsocComponent } from './page/gsoc/gsoc.component';
import { GsocProjectIdeaComponent } from './page/gsoc-project-idea/gsoc-project-idea.component';
import { ContributorSearchComponent } from './page/contributor-search/contributor-search.component';
-
-
+import { LoginComponent } from './page/login/login.component';
+import { RegisterComponent } from './page/register/register.component';
export const routes: Routes = [
{ path: '', component: HomepageComponent },
+ { path: 'login', component: LoginComponent },
+ { path: 'register', component: RegisterComponent },
{
path: 'projects',
component: ProjectsComponent,
@@ -31,6 +33,5 @@ export const routes: Routes = [
{ path: 'gsoc', component: GsocComponent },
{ path: 'idea', component: GsocProjectIdeaComponent },
{ path: 'search', component: ContributorSearchComponent },
-
];
-export const AppRoutingModule = RouterModule.forRoot(routes);
\ No newline at end of file
+export const AppRoutingModule = RouterModule.forRoot(routes);
diff --git a/webiu-ui/src/app/components/navbar/navbar.component.html b/webiu-ui/src/app/components/navbar/navbar.component.html
index 5ea18965..b9c690ca 100644
--- a/webiu-ui/src/app/components/navbar/navbar.component.html
+++ b/webiu-ui/src/app/components/navbar/navbar.component.html
@@ -26,14 +26,23 @@
GSoC
-
+
+
+
+
+
-
\ No newline at end of file
+
diff --git a/webiu-ui/src/app/components/navbar/navbar.component.ts b/webiu-ui/src/app/components/navbar/navbar.component.ts
index 6b590702..1fad3560 100644
--- a/webiu-ui/src/app/components/navbar/navbar.component.ts
+++ b/webiu-ui/src/app/components/navbar/navbar.component.ts
@@ -12,18 +12,31 @@ import { ThemeService } from '../../services/theme.service';
})
export class NavbarComponent {
isMenuOpen = false;
- isSunVisible = true;
+ isSunVisible = true;
+
toggleMenu() {
this.isMenuOpen = !this.isMenuOpen;
}
+
constructor(private themeService: ThemeService) {
this.isSunVisible = !this.themeService.isDarkMode();
}
+
toggleTheme(): void {
this.themeService.toggleDarkMode();
}
+
toggleMode() {
this.isSunVisible = !this.isSunVisible;
this.toggleTheme();
}
+
+ isLoggedIn(): boolean {
+ return !!localStorage.getItem('token');
+ }
+
+ logout() {
+ localStorage.removeItem('token');
+ localStorage.removeItem('user');
+ }
}
diff --git a/webiu-ui/src/app/page/login/login.component.html b/webiu-ui/src/app/page/login/login.component.html
new file mode 100644
index 00000000..0542edc7
--- /dev/null
+++ b/webiu-ui/src/app/page/login/login.component.html
@@ -0,0 +1,57 @@
+
+
+
+
Welcome back
+
Sign in to your account
+
+
+
+
+
+
+
+
+
+ Or continue with
+
+
+
+
+
+
+
+
+
+
+ Don't have an account? Register here
+
+
+
+
diff --git a/webiu-ui/src/app/page/login/login.component.scss b/webiu-ui/src/app/page/login/login.component.scss
new file mode 100644
index 00000000..1bf54c80
--- /dev/null
+++ b/webiu-ui/src/app/page/login/login.component.scss
@@ -0,0 +1,110 @@
+/* Main container styles */
+.min-h-screen {
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ background-color: var(--community-bg);
+ height: 80vh;
+}
+
+.card-container {
+ width: 100%;
+ max-width: 400px;
+ background-color: var(--community-card-bg);
+ padding: 1.5rem;
+ border-radius: 0.5rem;
+ box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);
+ margin-top: 10pt;
+ padding-top: 4rem;
+}
+
+/* Text and titles */
+.text-3xl {
+ font-size: 1.875rem;
+ font-weight: bold;
+}
+
+.text {
+ color: var(--primary-dark, var(--primary-dark, #0a0a15));
+}
+
+.text-center {
+ text-align: center;
+}
+
+/* Input label */
+.label {
+ font-size: 0.875rem;
+ color: #4b5563;
+}
+
+/* Input fields */
+.input-field {
+ width: 100%;
+ padding: 0.5rem;
+ border: 1px solid #d1d5db;
+ border-radius: 0.375rem;
+ outline: none;
+ transition: all 0.2s ease-in-out;
+}
+
+.input-field:focus {
+ border-color: #3b82f6;
+ ring: 2px solid rgba(59, 130, 246, 0.5);
+}
+
+/* Error message */
+.error-message {
+ color: #f87171;
+ font-size: 0.875rem;
+ margin-top: 0.25rem;
+}
+
+/* Button */
+.submit-button {
+ width: 100%;
+ background-color: #3b82f6;
+ color: white;
+ padding: 0.75rem;
+ border-radius: 0.375rem;
+ margin-top: 1rem;
+ cursor: pointer;
+ transition: background-color 0.2s ease;
+}
+
+.submit-button:hover {
+ background-color: #2563eb;
+}
+
+.submit-button:disabled {
+ background-color: #93c5fd;
+ cursor: not-allowed;
+}
+
+/* Divider */
+.divider {
+ position: relative;
+ margin: 1rem 0;
+}
+
+.divider-line {
+ width: 100%;
+ border-top: 1px solid #d1d5db;
+}
+
+/* Social buttons */
+.social-button {
+ width: 100%;
+ border: 2px solid #d1d5db;
+ color: #374151;
+ padding: 0.75rem;
+ border-radius: 0.375rem;
+ display: flex;
+ justify-content: center;
+ align-items: center;
+ transition: background-color 0.2s ease;
+}
+
+.social-button:hover {
+ background-color: #f3f4f6;
+}
diff --git a/webiu-ui/src/app/page/login/login.component.spec.ts b/webiu-ui/src/app/page/login/login.component.spec.ts
new file mode 100644
index 00000000..f80aa90a
--- /dev/null
+++ b/webiu-ui/src/app/page/login/login.component.spec.ts
@@ -0,0 +1,39 @@
+import { ComponentFixture, TestBed } from '@angular/core/testing';
+import { ReactiveFormsModule } from '@angular/forms';
+
+import { LoginComponent } from './login.component';
+
+describe('LoginComponent', () => {
+ let component: LoginComponent;
+ let fixture: ComponentFixture;
+
+ beforeEach(async () => {
+ await TestBed.configureTestingModule({
+ imports: [ReactiveFormsModule],
+ declarations: [LoginComponent]
+ }).compileComponents();
+
+ fixture = TestBed.createComponent(LoginComponent);
+ component = fixture.componentInstance;
+ fixture.detectChanges();
+ });
+
+ it('should create the component', () => {
+ expect(component).toBeTruthy();
+ });
+
+ it('should initialize the login form', () => {
+ expect(component.loginForm).toBeDefined();
+ expect(component.loginForm.get('email')).toBeTruthy();
+ });
+
+ it('should display an error message if email is invalid', () => {
+ const emailControl = component.loginForm.get('email');
+ emailControl?.setValue('invalid-email');
+ emailControl?.markAsTouched();
+
+ fixture.detectChanges();
+ const compiled = fixture.nativeElement;
+ expect(compiled.querySelector('mat-error').textContent).toContain('Please enter a valid email');
+ });
+});
diff --git a/webiu-ui/src/app/page/login/login.component.ts b/webiu-ui/src/app/page/login/login.component.ts
new file mode 100644
index 00000000..a4a89a9e
--- /dev/null
+++ b/webiu-ui/src/app/page/login/login.component.ts
@@ -0,0 +1,115 @@
+import { Component } from '@angular/core';
+import { FormBuilder, FormGroup, Validators } from '@angular/forms';
+import { CommonModule } from '@angular/common';
+import { ReactiveFormsModule } from '@angular/forms';
+import axios from 'axios';
+import { Router } from '@angular/router';
+
+@Component({
+ selector: 'app-login',
+ templateUrl: './login.component.html',
+ standalone: true,
+ styleUrls: ['./login.component.scss'],
+ imports: [CommonModule, ReactiveFormsModule],
+})
+export class LoginComponent {
+ loginForm: FormGroup;
+ isLoading = false;
+ errorMessage: string = '';
+
+ constructor(private fb: FormBuilder, private router: Router) {
+ this.loginForm = this.fb.group({
+ email: ['', [Validators.required, Validators.email]],
+ password: ['', Validators.required],
+ });
+
+ this.checkTokenAndRedirect();
+ }
+
+ async onSubmit() {
+ const userData = {
+ email: this.loginForm.value.email,
+ password: this.loginForm.value.password,
+ };
+
+ this.isLoading = true;
+ axios
+ .post('http://localhost:5100/api/auth/login', userData)
+ .then((response) => {
+ localStorage.setItem('token', response.data.data.token);
+ this.router.navigate(['/']);
+ })
+ .catch((error) => {
+ console.error('Login error:', error);
+ this.errorMessage = 'Invalid credentials. Please try again.';
+ this.isLoading = false;
+ });
+ }
+
+ signInWithGoogle() {
+ window.location.href = 'https://localhost:5100/api/auth/google';
+ }
+
+ signInWithGitHub() {
+ window.location.href = 'https://localhost:5100/api/auth/github';
+ }
+
+ redirectToRegister() {
+ this.router.navigate(['/register']);
+ }
+
+ private decodeToken(token: string) {
+ try {
+ const payload = JSON.parse(atob(token.split('.')[1]));
+ return payload;
+ } catch (error) {
+ console.error('Failed to decode token:', error);
+ return null;
+ }
+ }
+
+ private checkTokenAndRedirect() {
+ const token = localStorage.getItem('token');
+ if (token) {
+ const decodedToken = this.decodeToken(token);
+
+ if (decodedToken) {
+ const currentTime = Math.floor(Date.now() / 1000);
+
+ if (decodedToken.exp && decodedToken.exp > currentTime) {
+ console.log('User is already logged in. Redirecting to "/"...');
+ this.router.navigate(['/']);
+ return;
+ }
+ }
+
+ console.warn('Invalid or expired token. Clearing storage.');
+ localStorage.removeItem('token');
+ localStorage.removeItem('user');
+ }
+ }
+}
+
+function getQueryParams() {
+ const params = new URLSearchParams(window.location.search);
+ return {
+ token: params.get('token'),
+ id: params.get('id'),
+ name: params.get('name'),
+ email: params.get('email'),
+ };
+}
+
+function handleGoogleCallback() {
+ const { token, id, name, email } = getQueryParams();
+
+ if (token) {
+ localStorage.setItem('token', token);
+ localStorage.setItem('user', JSON.stringify({ id, name, email }));
+ window.location.href = '/';
+ }
+}
+
+if (window.location.search.includes('token=')) {
+ handleGoogleCallback();
+}
diff --git a/webiu-ui/src/app/page/register/register.component.html b/webiu-ui/src/app/page/register/register.component.html
new file mode 100644
index 00000000..ae3449c3
--- /dev/null
+++ b/webiu-ui/src/app/page/register/register.component.html
@@ -0,0 +1,61 @@
+
+
+
+
Create an Account
+
Fill in the details to register
+
+
+
+
+
+
+ Already have an account? Login here
+
+
+
+
+
\ No newline at end of file
diff --git a/webiu-ui/src/app/page/register/register.component.scss b/webiu-ui/src/app/page/register/register.component.scss
new file mode 100644
index 00000000..f1e2dd2b
--- /dev/null
+++ b/webiu-ui/src/app/page/register/register.component.scss
@@ -0,0 +1,110 @@
+/* Main container styles */
+.min-h-screen {
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ background-color: var(--community-bg);
+ height: 80vh;
+ }
+
+ .card-container {
+ width: 100%;
+ max-width: 400px;
+ background-color: var(--community-card-bg);
+ padding: 1.5rem;
+ border-radius: 0.5rem;
+ box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);
+ margin-top: 5rem;
+ color: var(--primary-dark, var(--primary-dark, #0a0a15)) !important;
+
+ }
+
+ /* Text and titles */
+ .text-3xl {
+ font-size: 1.875rem;
+ font-weight: bold;
+ }
+
+ .text{
+ color: var(--primary-dark, var(--primary-dark, #0a0a15)) !important;
+ }
+
+ .text-center {
+ text-align: center;
+ }
+
+ /* Input label */
+ .label {
+ font-size: 0.875rem;
+ }
+
+ /* Input fields */
+ .input-field {
+ width: 100%;
+ padding: 0.5rem;
+ border: 1px solid #d1d5db;
+ border-radius: 0.375rem;
+ outline: none;
+ transition: all 0.2s ease-in-out;
+ }
+
+ .input-field:focus {
+ border-color: #3b82f6;
+ ring: 2px solid rgba(59, 130, 246, 0.5);
+ }
+
+ /* Error message */
+ .error-message {
+ color: #f87171;
+ font-size: 0.875rem;
+ margin-top: 0.25rem;
+ }
+
+ /* Button */
+ .submit-button {
+ width: 100%;
+ background-color: #3b82f6;
+ color: white;
+ padding: 0.75rem;
+ border-radius: 0.375rem;
+ margin-top: 1rem;
+ cursor: pointer;
+ transition: background-color 0.2s ease;
+ }
+
+ .submit-button:hover {
+ background-color: #2563eb;
+ }
+
+ .submit-button:disabled {
+ background-color: #93c5fd;
+ cursor: not-allowed;
+ }
+
+ /* Divider */
+ .divider {
+ position: relative;
+ margin: 1rem 0;
+ }
+
+ .divider-line {
+ width: 100%;
+ border-top: 1px solid #d1d5db;
+ }
+
+ /* Social buttons */
+ .social-button {
+ width: 100%;
+ border: 2px solid #d1d5db;
+ padding: 0.75rem;
+ border-radius: 0.375rem;
+ display: flex;
+ justify-content: center;
+ align-items: center;
+ transition: background-color 0.2s ease;
+ }
+
+ .social-button:hover {
+ background-color: #f3f4f6;
+ }
+
\ No newline at end of file
diff --git a/webiu-ui/src/app/page/register/register.component.spec.ts b/webiu-ui/src/app/page/register/register.component.spec.ts
new file mode 100644
index 00000000..e69de29b
diff --git a/webiu-ui/src/app/page/register/register.component.ts b/webiu-ui/src/app/page/register/register.component.ts
new file mode 100644
index 00000000..9f8bfd20
--- /dev/null
+++ b/webiu-ui/src/app/page/register/register.component.ts
@@ -0,0 +1,53 @@
+import { Component } from '@angular/core';
+import { CommonModule } from '@angular/common';
+import { ReactiveFormsModule, FormBuilder, FormGroup, Validators } from '@angular/forms';
+import axios from 'axios';
+import { Router } from '@angular/router';
+
+@Component({
+ selector: 'app-register',
+ standalone: true,
+ templateUrl: './register.component.html',
+ styleUrls: ['./register.component.scss'],
+ imports: [CommonModule, ReactiveFormsModule],
+})
+export class RegisterComponent {
+ registerForm: FormGroup;
+ isLoading: boolean = false;
+ message: string = '';
+
+ constructor(private fb: FormBuilder, private router: Router) {
+ this.registerForm = this.fb.group({
+ name: ['', Validators.required],
+ email: ['', [Validators.required, Validators.email]],
+ password: ['', [Validators.required, Validators.minLength(6)]],
+ confirmPassword: ['', [Validators.required, Validators.minLength(6)]],
+ githubId: ['']
+ });
+ }
+
+ async onSubmit() {
+ if (this.registerForm.invalid) {
+ return;
+ }
+
+ this.isLoading = true;
+ const formData = this.registerForm.value;
+
+ try {
+ const response = await axios.post('http://localhost:5100/api/auth/register', formData);
+ this.message = 'User registered successfully!';
+ console.log('User registered:', response);
+ this.router.navigate(['/login']);
+ } catch (error) {
+ this.message = 'Error registering user. Please try again.';
+ console.error('Registration error:', error);
+ } finally {
+ this.isLoading = false;
+ }
+ }
+
+ redirectToLogin() {
+ this.router.navigate(['/login']);
+ }
+}
diff --git a/webiu-ui/src/app/services/auth.service.ts b/webiu-ui/src/app/services/auth.service.ts
new file mode 100644
index 00000000..bc2801b7
--- /dev/null
+++ b/webiu-ui/src/app/services/auth.service.ts
@@ -0,0 +1,60 @@
+// src/app/services/auth.service.ts
+
+import { Injectable } from '@angular/core';
+import { HttpClient } from '@angular/common/http';
+import { BehaviorSubject, Observable } from 'rxjs';
+import { catchError, tap } from 'rxjs/operators';
+import { environment } from '../../environments/environment'; // Replace with your environment path
+
+@Injectable({
+ providedIn: 'root',
+})
+export class AuthService {
+ private apiUrl = 'http://localhost:5100/api'; // Your backend API base URL
+ private currentUserSubject: BehaviorSubject;
+ public currentUser: Observable;
+
+ constructor(private http: HttpClient) {
+ this.currentUserSubject = new BehaviorSubject(JSON.parse(localStorage.getItem('currentUser') || '{}'));
+ this.currentUser = this.currentUserSubject.asObservable();
+ }
+
+ // Register a new user
+ register(userData: { name: string, email: string, password: string, confirmPassword: string }) {
+ const { name, email, password, confirmPassword } = userData;
+ return this.http.post(`${this.apiUrl}/auth/register`, { name, email, password, confirmPassword }).pipe(
+ catchError((error) => {
+ throw error;
+ })
+ );
+ }
+
+ // Login a user
+ login(email: string, password: string) {
+ return this.http.post(`${this.apiUrl}/auth/login`, { email, password }).pipe(
+ tap((user) => {
+ localStorage.setItem('currentUser', JSON.stringify(user));
+ this.currentUserSubject.next(user);
+ }),
+ catchError((error) => {
+ throw error;
+ })
+ );
+ }
+
+ // Logout the user
+ logout() {
+ localStorage.removeItem('currentUser');
+ this.currentUserSubject.next(null);
+ }
+
+ // Get the current logged-in user
+ get currentUserValue() {
+ return this.currentUserSubject.value;
+ }
+
+ // Check if the user is logged in
+ isLoggedIn(): boolean {
+ return !!this.currentUserSubject.value;
+ }
+}
diff --git a/webiu-ui/src/assets/login.svg b/webiu-ui/src/assets/login.svg
new file mode 100644
index 00000000..e62795c2
--- /dev/null
+++ b/webiu-ui/src/assets/login.svg
@@ -0,0 +1,14 @@
+
+
+
\ No newline at end of file
diff --git a/webiu-ui/src/index.html b/webiu-ui/src/index.html
index 4082f81f..608fb506 100644
--- a/webiu-ui/src/index.html
+++ b/webiu-ui/src/index.html
@@ -12,8 +12,11 @@
href="https://fonts.googleapis.com/css2?family=Poppins:ital,wght@0,100;0,200;0,300;0,400;0,500;0,600;0,700;0,800;0,900;1,100;1,200;1,300;1,400;1,500;1,600;1,700;1,800;1,900&display=swap"
rel="stylesheet"
/>
-
-
+
+
+
+
+