diff --git a/frontend/src/app/app.routes.ts b/frontend/src/app/app.routes.ts
index 5c57416..b4bf818 100644
--- a/frontend/src/app/app.routes.ts
+++ b/frontend/src/app/app.routes.ts
@@ -19,6 +19,20 @@ export const routes: Routes = [
(m) => m.VerifyEmailComponent
),
},
+ {
+ path: 'recover-password',
+ loadComponent: () =>
+ import('./feature/auth/recover-password/recover-password.component').then(
+ (m) => m.RecoverPasswordComponent
+ ),
+ },
+ {
+ path: 'reset-password',
+ loadComponent: () =>
+ import('./feature/auth/recover-password/recover-password.component').then(
+ (m) => m.RecoverPasswordComponent
+ ),
+ },
{
path: 'game/blackjack',
loadComponent: () => import('./feature/game/blackjack/blackjack.component'),
diff --git a/frontend/src/app/feature/auth/login/login.component.html b/frontend/src/app/feature/auth/login/login.component.html
index 5f4c535..dba76b2 100644
--- a/frontend/src/app/feature/auth/login/login.component.html
+++ b/frontend/src/app/feature/auth/login/login.component.html
@@ -83,6 +83,18 @@
+
+
+ Passwort vergessen?
+
+
+
+
Noch kein Konto?
diff --git a/frontend/src/app/feature/auth/login/login.component.ts b/frontend/src/app/feature/auth/login/login.component.ts
index b5a67a0..946f412 100644
--- a/frontend/src/app/feature/auth/login/login.component.ts
+++ b/frontend/src/app/feature/auth/login/login.component.ts
@@ -63,4 +63,9 @@ export class LoginComponent {
},
});
}
+
+ switchToForgotPassword() {
+ this.closeDialog.emit();
+ this.router.navigate(['/recover-password']);
+ }
}
diff --git a/frontend/src/app/feature/auth/recover-password/recover-password.component.html b/frontend/src/app/feature/auth/recover-password/recover-password.component.html
new file mode 100644
index 0000000..5879f3d
--- /dev/null
+++ b/frontend/src/app/feature/auth/recover-password/recover-password.component.html
@@ -0,0 +1,162 @@
+
+
+
+
+
+ @if (isResetMode()) {Passwort zurücksetzen} @else {Passwort vergessen}
+
+
+ @if (errorMessage()) {
+
+ {{ errorMessage() }}
+
+ }
+
+ @if (successMessage()) {
+
+ {{ successMessage() }}
+
+ }
+
+ @if (!isResetMode()) {
+
+
+ }
+
+ @if (isResetMode()) {
+
+
+ }
+
+
+
+
diff --git a/frontend/src/app/feature/auth/recover-password/recover-password.component.ts b/frontend/src/app/feature/auth/recover-password/recover-password.component.ts
new file mode 100644
index 0000000..fc3a4d9
--- /dev/null
+++ b/frontend/src/app/feature/auth/recover-password/recover-password.component.ts
@@ -0,0 +1,121 @@
+import { Component, EventEmitter, Output, signal } from '@angular/core';
+import { CommonModule } from '@angular/common';
+import { FormBuilder, FormGroup, ReactiveFormsModule, Validators } from '@angular/forms';
+import { ActivatedRoute, Router } from '@angular/router';
+import { AuthService } from '@service/auth.service';
+
+@Component({
+ selector: 'app-recover-password',
+ standalone: true,
+ imports: [
+ CommonModule,
+ ReactiveFormsModule,
+ ],
+ templateUrl: './recover-password.component.html',
+})
+export class RecoverPasswordComponent {
+ emailForm: FormGroup;
+ resetPasswordForm: FormGroup;
+ errorMessage = signal('');
+ successMessage = signal('');
+ isLoading = signal(false);
+ token = '';
+ isResetMode = signal(false);
+
+ @Output() closeDialog = new EventEmitter
();
+
+ constructor(
+ private fb: FormBuilder,
+ private authService: AuthService,
+ private router: Router,
+ private route: ActivatedRoute
+ ) {
+ this.emailForm = this.fb.group({
+ email: ['', [Validators.required, Validators.email]]
+ });
+
+ this.resetPasswordForm = this.fb.group({
+ password: ['', [Validators.required, Validators.minLength(8)]],
+ confirmPassword: ['', [Validators.required]]
+ }, {
+ validators: this.passwordMatchValidator
+ });
+
+ // Check if we're in reset mode
+ this.route.queryParamMap.subscribe(params => {
+ const token = params.get('token');
+ if (token) {
+ this.token = token;
+ this.isResetMode.set(true);
+ }
+ });
+ }
+
+ passwordMatchValidator(form: FormGroup) {
+ const password = form.get('password')?.value;
+ const confirmPassword = form.get('confirmPassword')?.value;
+ return password === confirmPassword ? null : { passwordMismatch: true };
+ }
+
+ get emailFormControls() {
+ return this.emailForm.controls;
+ }
+
+ get resetFormControls() {
+ return this.resetPasswordForm.controls;
+ }
+
+ onSubmitEmail(): void {
+ if (this.emailForm.invalid) {
+ return;
+ }
+
+ this.isLoading.set(true);
+ this.errorMessage.set('');
+ this.successMessage.set('');
+
+ const email = this.emailFormControls['email'].value;
+
+ this.authService.recoverPassword(email).subscribe({
+ next: () => {
+ this.isLoading.set(false);
+ this.successMessage.set('Wenn ein Konto mit dieser E-Mail existiert, wird eine E-Mail mit weiteren Anweisungen gesendet.');
+ this.emailForm.reset();
+ },
+ error: (err) => {
+ this.isLoading.set(false);
+ this.errorMessage.set(
+ err.error?.message || 'Ein Fehler ist aufgetreten. Bitte versuche es später erneut.'
+ );
+ }
+ });
+ }
+
+ onSubmitReset(): void {
+ if (this.resetPasswordForm.invalid) {
+ return;
+ }
+
+ this.isLoading.set(true);
+ this.errorMessage.set('');
+ this.successMessage.set('');
+
+ const password = this.resetFormControls['password'].value;
+
+ this.authService.resetPassword(this.token, password).subscribe({
+ next: () => {
+ this.isLoading.set(false);
+ this.successMessage.set('Dein Passwort wurde erfolgreich zurückgesetzt. Du kannst dich jetzt anmelden.');
+ setTimeout(() => {
+ this.router.navigate([''], { queryParams: { login: true } });
+ }, 3000);
+ },
+ error: (err) => {
+ this.isLoading.set(false);
+ this.errorMessage.set(
+ err.error?.message || 'Ein Fehler ist aufgetreten. Bitte versuche es später erneut.'
+ );
+ }
+ });
+ }
+}
diff --git a/frontend/src/app/service/auth.service.ts b/frontend/src/app/service/auth.service.ts
index 657067f..b51a4a1 100644
--- a/frontend/src/app/service/auth.service.ts
+++ b/frontend/src/app/service/auth.service.ts
@@ -77,6 +77,14 @@ export class AuthService {
public verifyEmail(token: string): Observable {
return this.http.post(`${this.authUrl}/verify?token=${token}`, null);
}
+
+ public recoverPassword(email: string): Observable {
+ return this.http.post(`${this.authUrl}/recover-password?email=${email}`, null);
+ }
+
+ public resetPassword(token: string, password: string): Observable {
+ return this.http.post(`${this.authUrl}/reset-password`, { token, password });
+ }
private setToken(token: string): void {
localStorage.setItem(TOKEN_KEY, token);