website refactor
This commit is contained in:
146
apps/website/lib/utilities/authValidation.ts
Normal file
146
apps/website/lib/utilities/authValidation.ts
Normal file
@@ -0,0 +1,146 @@
|
||||
/**
|
||||
* Auth Validation Utilities
|
||||
*
|
||||
* Pure functions for client-side validation of auth forms.
|
||||
* No side effects, synchronous.
|
||||
*/
|
||||
|
||||
export interface SignupFormData {
|
||||
firstName: string;
|
||||
lastName: string;
|
||||
email: string;
|
||||
password: string;
|
||||
confirmPassword: string;
|
||||
}
|
||||
|
||||
export interface ForgotPasswordFormData {
|
||||
email: string;
|
||||
}
|
||||
|
||||
export interface ResetPasswordFormData {
|
||||
newPassword: string;
|
||||
confirmPassword: string;
|
||||
}
|
||||
|
||||
export interface SignupValidationError {
|
||||
field: keyof SignupFormData;
|
||||
message: string;
|
||||
}
|
||||
|
||||
export interface ForgotPasswordValidationError {
|
||||
field: keyof ForgotPasswordFormData;
|
||||
message: string;
|
||||
}
|
||||
|
||||
export interface ResetPasswordValidationError {
|
||||
field: keyof ResetPasswordFormData;
|
||||
message: string;
|
||||
}
|
||||
|
||||
export class SignupFormValidation {
|
||||
static validateForm(data: SignupFormData): SignupValidationError[] {
|
||||
const errors: SignupValidationError[] = [];
|
||||
|
||||
// First name
|
||||
if (!data.firstName.trim()) {
|
||||
errors.push({ field: 'firstName', message: 'First name is required' });
|
||||
} else if (data.firstName.trim().length < 2) {
|
||||
errors.push({ field: 'firstName', message: 'First name must be at least 2 characters' });
|
||||
}
|
||||
|
||||
// Last name
|
||||
if (!data.lastName.trim()) {
|
||||
errors.push({ field: 'lastName', message: 'Last name is required' });
|
||||
} else if (data.lastName.trim().length < 2) {
|
||||
errors.push({ field: 'lastName', message: 'Last name must be at least 2 characters' });
|
||||
}
|
||||
|
||||
// Email
|
||||
if (!data.email.trim()) {
|
||||
errors.push({ field: 'email', message: 'Email is required' });
|
||||
} else if (!this.isValidEmail(data.email)) {
|
||||
errors.push({ field: 'email', message: 'Please enter a valid email address' });
|
||||
}
|
||||
|
||||
// Password
|
||||
if (!data.password) {
|
||||
errors.push({ field: 'password', message: 'Password is required' });
|
||||
} else if (data.password.length < 8) {
|
||||
errors.push({ field: 'password', message: 'Password must be at least 8 characters' });
|
||||
} else if (!this.hasValidPasswordComplexity(data.password)) {
|
||||
errors.push({ field: 'password', message: 'Password must contain at least one uppercase letter, one lowercase letter, and one number' });
|
||||
}
|
||||
|
||||
// Confirm password
|
||||
if (!data.confirmPassword) {
|
||||
errors.push({ field: 'confirmPassword', message: 'Please confirm your password' });
|
||||
} else if (data.password !== data.confirmPassword) {
|
||||
errors.push({ field: 'confirmPassword', message: 'Passwords do not match' });
|
||||
}
|
||||
|
||||
return errors;
|
||||
}
|
||||
|
||||
static generateDisplayName(firstName: string, lastName: string): string {
|
||||
const trimmedFirst = firstName.trim();
|
||||
const trimmedLast = lastName.trim();
|
||||
return `${trimmedFirst} ${trimmedLast}`.trim() || trimmedFirst || trimmedLast;
|
||||
}
|
||||
|
||||
private static isValidEmail(email: string): boolean {
|
||||
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
|
||||
return emailRegex.test(email);
|
||||
}
|
||||
|
||||
private static hasValidPasswordComplexity(password: string): boolean {
|
||||
return /[a-z]/.test(password) && /[A-Z]/.test(password) && /\d/.test(password);
|
||||
}
|
||||
}
|
||||
|
||||
export class ForgotPasswordFormValidation {
|
||||
static validateForm(data: ForgotPasswordFormData): ForgotPasswordValidationError[] {
|
||||
const errors: ForgotPasswordValidationError[] = [];
|
||||
|
||||
// Email
|
||||
if (!data.email.trim()) {
|
||||
errors.push({ field: 'email', message: 'Email is required' });
|
||||
} else if (!this.isValidEmail(data.email)) {
|
||||
errors.push({ field: 'email', message: 'Please enter a valid email address' });
|
||||
}
|
||||
|
||||
return errors;
|
||||
}
|
||||
|
||||
private static isValidEmail(email: string): boolean {
|
||||
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
|
||||
return emailRegex.test(email);
|
||||
}
|
||||
}
|
||||
|
||||
export class ResetPasswordFormValidation {
|
||||
static validateForm(data: ResetPasswordFormData): ResetPasswordValidationError[] {
|
||||
const errors: ResetPasswordValidationError[] = [];
|
||||
|
||||
// New password
|
||||
if (!data.newPassword) {
|
||||
errors.push({ field: 'newPassword', message: 'New password is required' });
|
||||
} else if (data.newPassword.length < 8) {
|
||||
errors.push({ field: 'newPassword', message: 'Password must be at least 8 characters' });
|
||||
} else if (!this.hasValidPasswordComplexity(data.newPassword)) {
|
||||
errors.push({ field: 'newPassword', message: 'Password must contain at least one uppercase letter, one lowercase letter, and one number' });
|
||||
}
|
||||
|
||||
// Confirm password
|
||||
if (!data.confirmPassword) {
|
||||
errors.push({ field: 'confirmPassword', message: 'Please confirm your new password' });
|
||||
} else if (data.newPassword !== data.confirmPassword) {
|
||||
errors.push({ field: 'confirmPassword', message: 'Passwords do not match' });
|
||||
}
|
||||
|
||||
return errors;
|
||||
}
|
||||
|
||||
private static hasValidPasswordComplexity(password: string): boolean {
|
||||
return /[a-z]/.test(password) && /[A-Z]/.test(password) && /\d/.test(password);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user