Skip to content

Commit 0ebc053

Browse files
committed
update components
1 parent 0ff2fd5 commit 0ebc053

23 files changed

+364
-343
lines changed

web/src/app/app.component.html

+2-336
Large diffs are not rendered by default.

web/src/app/app.component.ts

+5-4
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,12 @@
11
import { Component } from '@angular/core';
22
import { RouterOutlet } from '@angular/router';
3+
import { NavbarComponent } from './navbar/navbar.component';
34

45
@Component({
5-
selector: 'app-root',
6-
imports: [RouterOutlet],
7-
templateUrl: './app.component.html',
8-
styleUrl: './app.component.scss'
6+
selector: 'app-root',
7+
imports: [RouterOutlet, NavbarComponent],
8+
templateUrl: './app.component.html',
9+
styleUrl: './app.component.scss'
910
})
1011
export class AppComponent {
1112
title = 'resdeeds';

web/src/app/app.config.ts

+2-1
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,8 @@ import { ApplicationConfig, provideZoneChangeDetection } from '@angular/core';
22
import { provideRouter } from '@angular/router';
33

44
import { routes } from './app.routes';
5+
import { provideHttpClient, withInterceptorsFromDi } from '@angular/common/http';
56

67
export const appConfig: ApplicationConfig = {
7-
providers: [provideZoneChangeDetection({ eventCoalescing: true }), provideRouter(routes)]
8+
providers: [provideHttpClient(withInterceptorsFromDi()), provideZoneChangeDetection({ eventCoalescing: true }), provideRouter(routes)]
89
};

web/src/app/app.routes.ts

+18-2
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,19 @@
1-
import { Routes } from '@angular/router';
1+
import { NgModule } from '@angular/core';
2+
import { RouterModule, Routes } from '@angular/router';
3+
import { LoginComponent } from './login/login.component';
4+
import { SignupComponent } from './signup/signup.component';
5+
import { HelloWorldComponent } from './hello-world/hello-world.component';
6+
import { AuthGuard } from './services/auth.guard';
27

3-
export const routes: Routes = [];
8+
export const routes: Routes = [
9+
{ path: '', component: LoginComponent },
10+
{ path: 'signup', component: SignupComponent },
11+
{ path: 'hello-world', component: HelloWorldComponent, canActivate: [AuthGuard] },
12+
{ path: '**', redirectTo: '' }
13+
];
14+
15+
@NgModule({
16+
imports: [RouterModule.forRoot(routes)],
17+
exports: [RouterModule]
18+
})
19+
export class AppRoutingModule { }
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
<div class="container text-center mt-5">
2+
<h1>Hello World!</h1>
3+
<p>You are logged in.</p>
4+
</div>

web/src/app/hello-world/hello-world.component.scss

Whitespace-only changes.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
import { ComponentFixture, TestBed } from '@angular/core/testing';
2+
3+
import { HelloWorldComponent } from './hello-world.component';
4+
5+
describe('HelloWorldComponent', () => {
6+
let component: HelloWorldComponent;
7+
let fixture: ComponentFixture<HelloWorldComponent>;
8+
9+
beforeEach(async () => {
10+
await TestBed.configureTestingModule({
11+
imports: [HelloWorldComponent]
12+
})
13+
.compileComponents();
14+
15+
fixture = TestBed.createComponent(HelloWorldComponent);
16+
component = fixture.componentInstance;
17+
fixture.detectChanges();
18+
});
19+
20+
it('should create', () => {
21+
expect(component).toBeTruthy();
22+
});
23+
});
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
import { Component } from '@angular/core';
2+
3+
@Component({
4+
selector: 'app-hello-world',
5+
imports: [],
6+
templateUrl: './hello-world.component.html',
7+
styleUrl: './hello-world.component.scss'
8+
})
9+
export class HelloWorldComponent {
10+
11+
}
+14
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
<div class="container d-flex justify-content-center align-items-center vh-100">
2+
<div class="card p-4" style="width: 300px;">
3+
<h2 class="text-center mb-4">Login</h2>
4+
<form (ngSubmit)="onSubmit()">
5+
<div class="mb-3">
6+
<input type="email" class="form-control" placeholder="Email" [(ngModel)]="email" name="email" required>
7+
</div>
8+
<div class="mb-3">
9+
<input type="password" class="form-control" placeholder="Password" [(ngModel)]="password" name="password" required>
10+
</div>
11+
<button type="submit" class="btn btn-primary w-100">Login</button>
12+
</form>
13+
</div>
14+
</div>

web/src/app/login/login.component.scss

Whitespace-only changes.
+23
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
import { ComponentFixture, TestBed } from '@angular/core/testing';
2+
3+
import { LoginComponent } from './login.component';
4+
5+
describe('LoginComponent', () => {
6+
let component: LoginComponent;
7+
let fixture: ComponentFixture<LoginComponent>;
8+
9+
beforeEach(async () => {
10+
await TestBed.configureTestingModule({
11+
imports: [LoginComponent]
12+
})
13+
.compileComponents();
14+
15+
fixture = TestBed.createComponent(LoginComponent);
16+
component = fixture.componentInstance;
17+
fixture.detectChanges();
18+
});
19+
20+
it('should create', () => {
21+
expect(component).toBeTruthy();
22+
});
23+
});

web/src/app/login/login.component.ts

+29
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
import { Component } from '@angular/core';
2+
import { Router } from '@angular/router';
3+
import { AuthService } from '../services/auth.service';
4+
import { FormsModule } from '@angular/forms';
5+
6+
@Component({
7+
imports: [FormsModule],
8+
selector: 'app-login',
9+
templateUrl: './login.component.html',
10+
styleUrls: ['./login.component.css']
11+
})
12+
export class LoginComponent {
13+
email = '';
14+
password = '';
15+
16+
constructor(private authService: AuthService, private router: Router) { }
17+
18+
onSubmit() {
19+
this.authService.login(this.email, this.password).subscribe({
20+
next: (response: any) => {
21+
localStorage.setItem('token', response.token);
22+
this.router.navigate(['/hello-world']);
23+
},
24+
error: (error) => {
25+
console.error('Login failed', error);
26+
}
27+
});
28+
}
29+
}
+18
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
<nav class="navbar navbar-expand-lg navbar-dark bg-primary">
2+
<div class="container-fluid">
3+
<a class="navbar-brand" href="#">ResDEEDS</a>
4+
<div class="collapse navbar-collapse" id="navbarNav">
5+
<ul class="navbar-nav ms-auto">
6+
<li class="nav-item">
7+
<a *ngIf="!isLoggedIn" class="nav-link" routerLink="/">Login</a>
8+
</li>
9+
<li class="nav-item">
10+
<a *ngIf="!isLoggedIn" class="nav-link" routerLink="/signup">Sign Up</a>
11+
</li>
12+
<li class="nav-item">
13+
<a *ngIf="isLoggedIn" class="nav-link" (click)="logout()">Logout</a>
14+
</li>
15+
</ul>
16+
</div>
17+
</div>
18+
</nav>

web/src/app/navbar/navbar.component.scss

Whitespace-only changes.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
import { ComponentFixture, TestBed } from '@angular/core/testing';
2+
3+
import { NavbarComponent } from './navbar.component';
4+
5+
describe('NavbarComponent', () => {
6+
let component: NavbarComponent;
7+
let fixture: ComponentFixture<NavbarComponent>;
8+
9+
beforeEach(async () => {
10+
await TestBed.configureTestingModule({
11+
imports: [NavbarComponent]
12+
})
13+
.compileComponents();
14+
15+
fixture = TestBed.createComponent(NavbarComponent);
16+
component = fixture.componentInstance;
17+
fixture.detectChanges();
18+
});
19+
20+
it('should create', () => {
21+
expect(component).toBeTruthy();
22+
});
23+
});
+22
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
import { Component } from '@angular/core';
2+
import { AuthService } from '../services/auth.service';
3+
import { CommonModule } from '@angular/common';
4+
5+
@Component({
6+
selector: 'app-navbar',
7+
standalone: true,
8+
imports: [CommonModule],
9+
templateUrl: './navbar.component.html',
10+
styleUrls: ['./navbar.component.css']
11+
})
12+
export class NavbarComponent {
13+
isLoggedIn: boolean = false;
14+
15+
constructor(private authService: AuthService) {
16+
this.isLoggedIn = this.authService.isLoggedInSignalValue();
17+
}
18+
19+
logout() {
20+
this.authService.logout();
21+
}
22+
}

web/src/app/services/auth.guard.spec.ts

Whitespace-only changes.

web/src/app/services/auth.guard.ts

+19
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
import { Injectable } from '@angular/core';
2+
import { CanActivate, Router } from '@angular/router';
3+
import { AuthService } from './auth.service';
4+
5+
@Injectable({
6+
providedIn: 'root'
7+
})
8+
export class AuthGuard implements CanActivate {
9+
constructor(private authService: AuthService, private router: Router) { }
10+
11+
canActivate(): boolean {
12+
if (this.authService.isLoggedIn()) {
13+
return true;
14+
} else {
15+
this.router.navigate(['/']);
16+
return false;
17+
}
18+
}
19+
}

web/src/app/services/auth.service.ts

+35
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
import { Injectable, signal } from '@angular/core';
2+
import { HttpClient } from '@angular/common/http';
3+
import { Router } from '@angular/router';
4+
5+
@Injectable({
6+
providedIn: 'root'
7+
})
8+
export class AuthService {
9+
private apiUrl = 'http://localhost:5050';
10+
private isLoggedInSignal = signal(false);
11+
12+
constructor(private http: HttpClient, private router: Router) { }
13+
14+
isLoggedIn(): boolean {
15+
return !!localStorage.getItem('token');
16+
}
17+
18+
get isLoggedInSignalValue() {
19+
return this.isLoggedInSignal;
20+
}
21+
22+
login(email: string, password: string) {
23+
return this.http.post(`${this.apiUrl}/api/auth/login/`, { email, password });
24+
}
25+
26+
signup(email: string, password: string) {
27+
return this.http.post(`${this.apiUrl}api/auth/register/`, { email, password });
28+
}
29+
30+
logout() {
31+
localStorage.removeItem('token');
32+
this.router.navigate(['/']);
33+
return this.isLoggedInSignal();
34+
}
35+
}
+43
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
<div class="container d-flex justify-content-center align-items-center vh-100">
2+
<div class="card p-4" style="width: 300px;">
3+
<h2 class="text-center mb-4">Sign Up</h2>
4+
<form [formGroup]="signupForm" (ngSubmit)="onSubmit()">
5+
<!-- Email Field -->
6+
<div class="mb-3">
7+
<input type="email" class="form-control" placeholder="Email" formControlName="email">
8+
<div *ngIf="signupForm.get('email')?.invalid && signupForm.get('email')?.touched" class="text-danger">
9+
<small *ngIf="signupForm.get('email')?.errors?.['required']">Email is required.</small>
10+
<small *ngIf="signupForm.get('email')?.errors?.['email']">Invalid email format.</small>
11+
</div>
12+
</div>
13+
14+
<!-- Password Field -->
15+
<div class="mb-3">
16+
<input type="password" class="form-control" placeholder="Password" formControlName="password">
17+
<div *ngIf="signupForm.get('password')?.invalid && signupForm.get('password')?.touched" class="text-danger">
18+
<small *ngIf="signupForm.get('password')?.errors?.['required']">Password is required.</small>
19+
<small *ngIf="signupForm.get('password')?.errors?.['minlength']">Password must be at least 6 characters.</small>
20+
</div>
21+
</div>
22+
23+
<!-- Verify Password Field -->
24+
<div class="mb-3">
25+
<input type="password" class="form-control" placeholder="Verify Password" formControlName="verifyPassword">
26+
<div *ngIf="signupForm.get('verifyPassword')?.invalid && signupForm.get('verifyPassword')?.touched" class="text-danger">
27+
<small *ngIf="signupForm.get('verifyPassword')?.errors?.['required']">Verify Password is required.</small>
28+
</div>
29+
<div *ngIf="signupForm.errors?.['mismatch'] && signupForm.get('verifyPassword')?.touched" class="text-danger">
30+
<small>Passwords do not match.</small>
31+
</div>
32+
</div>
33+
34+
<!-- Submit Button -->
35+
<button type="submit" class="btn btn-primary w-100" [disabled]="signupForm.invalid">Sign Up</button>
36+
</form>
37+
38+
<!-- Link to Login Page -->
39+
<div class="mt-3 text-center">
40+
<p>Already have an account? <a routerLink="/login">Login</a></p>
41+
</div>
42+
</div>
43+
</div>

web/src/app/signup/signup.component.scss

Whitespace-only changes.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
import { ComponentFixture, TestBed } from '@angular/core/testing';
2+
3+
import { SignupComponent } from './signup.component';
4+
5+
describe('SignupComponent', () => {
6+
let component: SignupComponent;
7+
let fixture: ComponentFixture<SignupComponent>;
8+
9+
beforeEach(async () => {
10+
await TestBed.configureTestingModule({
11+
imports: [SignupComponent]
12+
})
13+
.compileComponents();
14+
15+
fixture = TestBed.createComponent(SignupComponent);
16+
component = fixture.componentInstance;
17+
fixture.detectChanges();
18+
});
19+
20+
it('should create', () => {
21+
expect(component).toBeTruthy();
22+
});
23+
});

0 commit comments

Comments
 (0)