diff --git a/web/src/app/routes/dashboard/account/account.component.html b/web/src/app/routes/dashboard/account/account.component.html
new file mode 100644
index 0000000..67666c1
--- /dev/null
+++ b/web/src/app/routes/dashboard/account/account.component.html
@@ -0,0 +1,9 @@
+
Actions
+
+
logout
+
Change Password
+
+
+
+
+
diff --git a/web/src/app/routes/dashboard/account/account.component.spec.ts b/web/src/app/routes/dashboard/account/account.component.spec.ts
new file mode 100644
index 0000000..1ec4ce5
--- /dev/null
+++ b/web/src/app/routes/dashboard/account/account.component.spec.ts
@@ -0,0 +1,23 @@
+import { ComponentFixture, TestBed } from '@angular/core/testing';
+
+import { AccountComponent } from './account.component';
+
+describe('AccountComponent', () => {
+ let component: AccountComponent;
+ let fixture: ComponentFixture
;
+
+ beforeEach(async () => {
+ await TestBed.configureTestingModule({
+ imports: [AccountComponent]
+ })
+ .compileComponents();
+
+ fixture = TestBed.createComponent(AccountComponent);
+ component = fixture.componentInstance;
+ fixture.detectChanges();
+ });
+
+ it('should create', () => {
+ expect(component).toBeTruthy();
+ });
+});
diff --git a/web/src/app/routes/dashboard/account/account.component.ts b/web/src/app/routes/dashboard/account/account.component.ts
new file mode 100644
index 0000000..d3cba20
--- /dev/null
+++ b/web/src/app/routes/dashboard/account/account.component.ts
@@ -0,0 +1,19 @@
+import { ChangeDetectionStrategy, Component, inject } from '@angular/core';
+import { AuthService } from '../../../shared/services/auth.service';
+import { ButtonComponent } from '../../../components/button/button.component';
+import { ModalComponent } from '../../../components/modal/modal.component';
+import { ChangePasswordComponent } from '../../../components/change-password/change-password.component';
+
+@Component({
+ selector: 'app-account',
+ imports: [ButtonComponent, ModalComponent, ChangePasswordComponent],
+ templateUrl: './account.component.html',
+})
+export class AccountComponent {
+ auth = inject(AuthService);
+ changePWOpen: boolean = false;
+
+ changePW() {
+ this.changePWOpen = true;
+ }
+}
diff --git a/web/src/app/routes/dashboard/dashboard.component.html b/web/src/app/routes/dashboard/dashboard.component.html
index 29d1db0..30dcd41 100644
--- a/web/src/app/routes/dashboard/dashboard.component.html
+++ b/web/src/app/routes/dashboard/dashboard.component.html
@@ -18,17 +18,18 @@
TL;DR; {{ post.tldr }}
-
Edit
-
-
+
diff --git a/web/src/app/routes/dashboard/dashboard.component.ts b/web/src/app/routes/dashboard/dashboard.component.ts
index 7d27414..3368765 100644
--- a/web/src/app/routes/dashboard/dashboard.component.ts
+++ b/web/src/app/routes/dashboard/dashboard.component.ts
@@ -1,12 +1,12 @@
import { Component, effect, inject } from '@angular/core';
-import { PostEditorComponent } from '../../components/post-editor/post-editor.component';
import { NgFor } from '@angular/common';
import { PostsService } from '../../shared/services/posts.service';
-import { RouterLink, RouterOutlet } from '@angular/router';
+import { RouterLink } from '@angular/router';
+import { ButtonComponent } from '../../components/button/button.component';
@Component({
selector: 'app-admin',
- imports: [NgFor, RouterLink],
+ imports: [NgFor, RouterLink, ButtonComponent],
standalone: true,
templateUrl: './dashboard.component.html',
})
diff --git a/web/src/app/shared/services/auth.service.ts b/web/src/app/shared/services/auth.service.ts
index d151a93..766cb50 100644
--- a/web/src/app/shared/services/auth.service.ts
+++ b/web/src/app/shared/services/auth.service.ts
@@ -8,6 +8,7 @@ import {
} from '@angular/core';
import { LoginResponse, User, Claims } from '../interfaces/auth';
import { environment } from '../../../environments/environment';
+import { ActivatedRouteSnapshot, Router } from '@angular/router';
const JWT_KEY = 'token';
@@ -16,6 +17,7 @@ const JWT_KEY = 'token';
})
export class AuthService {
private http = inject(HttpClient);
+ private router = inject(Router);
jwt: WritableSignal = signal(null);
claims: WritableSignal = signal(null);
timeout: any | null = null;
@@ -76,9 +78,32 @@ export class AuthService {
.post(`${environment.apiRoot}/auth/login`, user)
.subscribe((res) => this.jwt.set(res.token));
}
+
+ signup(user: User) {
+ this.http
+ .post(`${environment.apiRoot}/auth/signup`, user)
+ .subscribe((res) => this.jwt.set(res.token));
+ }
+
+ changePassword(password: string): Promise {
+ return new Promise((resolve) => {
+ this.http
+ .put(`${environment.apiRoot}/auth/password`, { password: password })
+ .subscribe({
+ complete: () => resolve(true),
+ error: () => resolve(false),
+ });
+ });
+ }
+
logout() {
this.http.delete(`${environment.apiRoot}/auth/logout`).subscribe(() => {
this.jwt.set(null);
+
+ // move away if protected
+ if (isOnProtectedRoute(this.router.routerState.snapshot.root)) {
+ this.router.navigateByUrl('/');
+ }
});
}
@@ -111,3 +136,11 @@ function extractExpiration(token: string): Date {
const jwt = parseJwt(token);
return new Date(jwt.exp * 1000);
}
+function isOnProtectedRoute(route: ActivatedRouteSnapshot): boolean {
+ // If this segment has the flag, return true
+ if (route.data && route.data['requiresAuth']) {
+ return true;
+ }
+ // Recursively check children
+ return route.children.some((child) => isOnProtectedRoute(child));
+}