optimize account creation and verification
This commit is contained in:
@@ -18,6 +18,11 @@ export const routes: Routes = [
|
||||
loadComponent: () => import('./auth/register/register.component').then(m => m.RegisterComponent),
|
||||
canActivate: [LoginGuard],
|
||||
},
|
||||
{
|
||||
path: 'resend-verification',
|
||||
loadComponent: () => import('./auth/resend-verification/resend-verification.component').then(m => m.ResendVerificationComponent),
|
||||
canActivate: [LoginGuard],
|
||||
},
|
||||
{
|
||||
path: 'dashboard',
|
||||
loadComponent: () => import('./dashboard/dashboard.component').then(m => m.DashboardComponent),
|
||||
|
||||
@@ -64,6 +64,9 @@ import { NgIf } from '@angular/common';
|
||||
Don't have an account?
|
||||
<a routerLink="/register" class="text-indigo-600 font-medium hover:underline">Register</a>
|
||||
</p>
|
||||
<p class="text-center mt-2 text-sm text-gray-500">
|
||||
<a routerLink="/resend-verification" class="text-indigo-600 font-medium hover:underline">Resend verification email</a>
|
||||
</p>
|
||||
</mat-card>
|
||||
</div>
|
||||
`,
|
||||
|
||||
@@ -26,12 +26,6 @@ import { AuthService } from '../../core/services/auth.service';
|
||||
</div>
|
||||
|
||||
<form [formGroup]="registerForm" (ngSubmit)="onSubmit()" class="flex flex-col gap-4">
|
||||
<mat-form-field appearance="outline" class="w-full">
|
||||
<mat-label>Name</mat-label>
|
||||
<input matInput formControlName="name" placeholder="Your name" />
|
||||
<mat-error *ngIf="registerForm.get('name')?.hasError('required')">Name is required</mat-error>
|
||||
</mat-form-field>
|
||||
|
||||
<mat-form-field appearance="outline" class="w-full">
|
||||
<mat-label>Email</mat-label>
|
||||
<input matInput type="email" formControlName="email" placeholder="you@example.com" />
|
||||
@@ -39,13 +33,6 @@ import { AuthService } from '../../core/services/auth.service';
|
||||
<mat-error *ngIf="registerForm.get('email')?.hasError('email')">Invalid email format</mat-error>
|
||||
</mat-form-field>
|
||||
|
||||
<mat-form-field appearance="outline" class="w-full">
|
||||
<mat-label>Password</mat-label>
|
||||
<input matInput type="password" formControlName="password" />
|
||||
<mat-error *ngIf="registerForm.get('password')?.hasError('required')">Password is required</mat-error>
|
||||
<mat-error *ngIf="registerForm.get('password')?.hasError('minlength')">At least 8 characters</mat-error>
|
||||
</mat-form-field>
|
||||
|
||||
<div *ngIf="error" class="text-red-600 text-sm">{{ error }}</div>
|
||||
|
||||
<button mat-flat-button color="primary" type="submit" [disabled]="loading" class="w-full py-2">
|
||||
@@ -66,9 +53,7 @@ import { AuthService } from '../../core/services/auth.service';
|
||||
})
|
||||
export class RegisterComponent {
|
||||
registerForm = this.fb.group({
|
||||
name: ['', Validators.required],
|
||||
email: ['', [Validators.required, Validators.email]],
|
||||
password: ['', [Validators.required, Validators.minLength(8)]],
|
||||
});
|
||||
loading = false;
|
||||
error = '';
|
||||
@@ -83,8 +68,8 @@ export class RegisterComponent {
|
||||
if (this.registerForm.invalid) return;
|
||||
this.loading = true;
|
||||
this.error = '';
|
||||
const { name, email, password } = this.registerForm.value;
|
||||
this.authService.register(name!, email!, password!).subscribe({
|
||||
const { email } = this.registerForm.value;
|
||||
this.authService.register(email!).subscribe({
|
||||
next: () => this.router.navigate(['/verify-email'], { queryParams: { email: email! } }),
|
||||
error: (err) => {
|
||||
this.error = err.error?.message || err.error?.error || 'Registration failed';
|
||||
|
||||
@@ -0,0 +1,83 @@
|
||||
import { Component } from '@angular/core';
|
||||
import { RouterLink } from '@angular/router';
|
||||
import { FormBuilder, ReactiveFormsModule, Validators } from '@angular/forms';
|
||||
import { MatCardModule } from '@angular/material/card';
|
||||
import { MatFormFieldModule } from '@angular/material/form-field';
|
||||
import { MatInputModule } from '@angular/material/input';
|
||||
import { MatButtonModule } from '@angular/material/button';
|
||||
import { MatProgressSpinnerModule } from '@angular/material/progress-spinner';
|
||||
import { NgIf } from '@angular/common';
|
||||
import { AuthService } from '../../core/services/auth.service';
|
||||
|
||||
@Component({
|
||||
selector: 'app-resend-verification',
|
||||
standalone: true,
|
||||
imports: [
|
||||
RouterLink, ReactiveFormsModule, NgIf,
|
||||
MatCardModule, MatFormFieldModule, MatInputModule, MatButtonModule,
|
||||
MatProgressSpinnerModule,
|
||||
],
|
||||
template: `
|
||||
<div class="min-h-screen flex items-center justify-center bg-gray-50 px-4">
|
||||
<mat-card class="w-full max-w-md p-8">
|
||||
<div class="text-center mb-8">
|
||||
<h1 class="text-3xl font-bold text-indigo-600">Indie</h1>
|
||||
<p class="text-gray-500 mt-1">Resend verification email</p>
|
||||
</div>
|
||||
|
||||
<form [formGroup]="resendForm" (ngSubmit)="onSubmit()" class="flex flex-col gap-4">
|
||||
<mat-form-field appearance="outline" class="w-full">
|
||||
<mat-label>Email</mat-label>
|
||||
<input matInput type="email" formControlName="email" placeholder="you@example.com" />
|
||||
<mat-error *ngIf="resendForm.get('email')?.hasError('required')">Email is required</mat-error>
|
||||
<mat-error *ngIf="resendForm.get('email')?.hasError('email')">Invalid email format</mat-error>
|
||||
</mat-form-field>
|
||||
|
||||
<div *ngIf="message" [class.text-green-600]="success" [class.text-red-600]="!success" class="text-sm text-center">{{ message }}</div>
|
||||
|
||||
<button mat-flat-button color="primary" type="submit" [disabled]="loading" class="w-full py-2">
|
||||
<span class="flex items-center justify-center">
|
||||
<mat-spinner *ngIf="loading" diameter="20" class="mr-2"></mat-spinner>
|
||||
{{ loading ? 'Sending...' : 'Send verification email' }}
|
||||
</span>
|
||||
</button>
|
||||
</form>
|
||||
|
||||
<p class="text-center mt-6 text-sm text-gray-500">
|
||||
<a routerLink="/login" class="text-indigo-600 font-medium hover:underline">Back to login</a>
|
||||
</p>
|
||||
</mat-card>
|
||||
</div>
|
||||
`,
|
||||
})
|
||||
export class ResendVerificationComponent {
|
||||
resendForm = this.fb.group({
|
||||
email: ['', [Validators.required, Validators.email]],
|
||||
});
|
||||
loading = false;
|
||||
message = '';
|
||||
success = false;
|
||||
|
||||
constructor(
|
||||
private fb: FormBuilder,
|
||||
private authService: AuthService,
|
||||
) {}
|
||||
|
||||
onSubmit(): void {
|
||||
if (this.resendForm.invalid) return;
|
||||
this.loading = true;
|
||||
this.message = '';
|
||||
this.authService.resendVerification(this.resendForm.value.email!).subscribe({
|
||||
next: () => {
|
||||
this.message = 'If this email is registered, a verification link has been sent.';
|
||||
this.success = true;
|
||||
this.loading = false;
|
||||
},
|
||||
error: () => {
|
||||
this.message = 'Failed to send. Please try again later.';
|
||||
this.success = false;
|
||||
this.loading = false;
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -62,8 +62,14 @@ type PageState = 'check-email' | 'verifying' | 'verified' | 'set-password' | 'er
|
||||
</div>
|
||||
|
||||
<div *ngIf="state === 'set-password'">
|
||||
<h2 class="text-xl font-semibold text-gray-800 mb-4 text-center">Set your password</h2>
|
||||
<h2 class="text-xl font-semibold text-gray-800 mb-4 text-center">Set your name and password</h2>
|
||||
<form [formGroup]="passwordForm" (ngSubmit)="onSetPassword()" class="flex flex-col gap-4">
|
||||
<mat-form-field appearance="outline" class="w-full">
|
||||
<mat-label>Name</mat-label>
|
||||
<input matInput formControlName="name" placeholder="Your name" />
|
||||
<mat-error *ngIf="passwordForm.get('name')?.hasError('required')">Name is required</mat-error>
|
||||
</mat-form-field>
|
||||
|
||||
<mat-form-field appearance="outline" class="w-full">
|
||||
<mat-label>New Password</mat-label>
|
||||
<input matInput type="password" formControlName="password" />
|
||||
@@ -120,6 +126,7 @@ export class VerifyEmailComponent implements OnInit {
|
||||
passwordError = '';
|
||||
|
||||
passwordForm = this.fb.group({
|
||||
name: ['', Validators.required],
|
||||
password: ['', [Validators.required, Validators.minLength(8)]],
|
||||
confirmPassword: ['', Validators.required],
|
||||
}, { validators: this.passwordsMatch });
|
||||
@@ -211,7 +218,7 @@ export class VerifyEmailComponent implements OnInit {
|
||||
this.passwordLoading = true;
|
||||
this.passwordError = '';
|
||||
|
||||
this.authService.setPassword(this.setupToken, this.passwordForm.value.password!).subscribe({
|
||||
this.authService.setPassword(this.setupToken, this.passwordForm.value.password!, this.passwordForm.value.name!).subscribe({
|
||||
next: () => {
|
||||
this.router.navigate(['/login'], { queryParams: { verified: 'true' } });
|
||||
},
|
||||
|
||||
@@ -14,8 +14,8 @@ export class AuthService {
|
||||
|
||||
constructor(private http: HttpClient) {}
|
||||
|
||||
register(name: string, email: string, password: string): Observable<{ message: string }> {
|
||||
return this.http.post<{ message: string }>('/api/auth/register', { name, email, password });
|
||||
register(email: string): Observable<{ message: string }> {
|
||||
return this.http.post<{ message: string }>('/api/auth/register', { email });
|
||||
}
|
||||
|
||||
login(email: string, password: string): Observable<AuthResponse> {
|
||||
@@ -41,8 +41,8 @@ export class AuthService {
|
||||
return this.http.post<{ message: string }>('/api/auth/resend-verification', { email });
|
||||
}
|
||||
|
||||
setPassword(setupToken: string, password: string): Observable<void> {
|
||||
return this.http.post<void>('/api/auth/set-password', { setupToken, password });
|
||||
setPassword(setupToken: string, password: string, name: string): Observable<void> {
|
||||
return this.http.post<void>('/api/auth/set-password', { setupToken, password, name });
|
||||
}
|
||||
|
||||
getAccessToken(): string | null {
|
||||
|
||||
Reference in New Issue
Block a user