chore: remove cashed
This commit is contained in:
6
frontend/src/utils/cn.ts
Normal file
6
frontend/src/utils/cn.ts
Normal file
@@ -0,0 +1,6 @@
|
||||
import { type ClassValue, clsx } from 'clsx'
|
||||
import { twMerge } from 'tailwind-merge'
|
||||
|
||||
export function cn(...inputs: ClassValue[]) {
|
||||
return twMerge(clsx(inputs))
|
||||
}
|
||||
80
frontend/src/utils/passwordStrength.ts
Normal file
80
frontend/src/utils/passwordStrength.ts
Normal file
@@ -0,0 +1,80 @@
|
||||
export interface PasswordStrength {
|
||||
score: number; // 0-4
|
||||
label: string;
|
||||
color: string; // Tailwind color class prefix (e.g., 'red', 'yellow', 'green')
|
||||
feedback: string[];
|
||||
}
|
||||
|
||||
export function calculatePasswordStrength(password: string): PasswordStrength {
|
||||
let score = 0;
|
||||
const feedback: string[] = [];
|
||||
|
||||
if (!password) {
|
||||
return {
|
||||
score: 0,
|
||||
label: 'Empty',
|
||||
color: 'gray',
|
||||
feedback: [],
|
||||
};
|
||||
}
|
||||
|
||||
// Length check
|
||||
if (password.length < 8) {
|
||||
feedback.push('Too short (min 8 chars)');
|
||||
} else {
|
||||
score += 1;
|
||||
}
|
||||
|
||||
if (password.length >= 12) {
|
||||
score += 1;
|
||||
}
|
||||
|
||||
// Complexity checks
|
||||
const hasLower = /[a-z]/.test(password);
|
||||
const hasUpper = /[A-Z]/.test(password);
|
||||
const hasNumber = /\d/.test(password);
|
||||
const hasSpecial = /[^A-Za-z0-9]/.test(password);
|
||||
|
||||
const varietyCount = [hasLower, hasUpper, hasNumber, hasSpecial].filter(Boolean).length;
|
||||
|
||||
if (varietyCount >= 3) {
|
||||
score += 1;
|
||||
}
|
||||
if (varietyCount === 4) {
|
||||
score += 1;
|
||||
}
|
||||
|
||||
// Penalties
|
||||
if (varietyCount < 2 && password.length >= 8) {
|
||||
feedback.push('Add more variety (uppercase, numbers, symbols)');
|
||||
}
|
||||
|
||||
// Cap score at 4
|
||||
score = Math.min(score, 4);
|
||||
|
||||
// Determine label and color
|
||||
let label = 'Very Weak';
|
||||
let color = 'red';
|
||||
|
||||
switch (score) {
|
||||
case 0:
|
||||
case 1:
|
||||
label = 'Weak';
|
||||
color = 'red';
|
||||
break;
|
||||
case 2:
|
||||
label = 'Fair';
|
||||
color = 'yellow';
|
||||
break;
|
||||
case 3:
|
||||
label = 'Good';
|
||||
color = 'green';
|
||||
break;
|
||||
case 4:
|
||||
label = 'Strong';
|
||||
color = 'green';
|
||||
break;
|
||||
}
|
||||
|
||||
return { score, label, color, feedback };
|
||||
}
|
||||
29
frontend/src/utils/toast.ts
Normal file
29
frontend/src/utils/toast.ts
Normal file
@@ -0,0 +1,29 @@
|
||||
type ToastType = 'success' | 'error' | 'info' | 'warning'
|
||||
|
||||
export interface Toast {
|
||||
id: number
|
||||
message: string
|
||||
type: ToastType
|
||||
}
|
||||
|
||||
let toastId = 0
|
||||
export const toastCallbacks = new Set<(toast: Toast) => void>()
|
||||
|
||||
export const toast = {
|
||||
success: (message: string) => {
|
||||
const id = ++toastId
|
||||
toastCallbacks.forEach(callback => callback({ id, message, type: 'success' }))
|
||||
},
|
||||
error: (message: string) => {
|
||||
const id = ++toastId
|
||||
toastCallbacks.forEach(callback => callback({ id, message, type: 'error' }))
|
||||
},
|
||||
info: (message: string) => {
|
||||
const id = ++toastId
|
||||
toastCallbacks.forEach(callback => callback({ id, message, type: 'info' }))
|
||||
},
|
||||
warning: (message: string) => {
|
||||
const id = ++toastId
|
||||
toastCallbacks.forEach(callback => callback({ id, message, type: 'warning' }))
|
||||
},
|
||||
}
|
||||
4
frontend/src/utils/validation.ts
Normal file
4
frontend/src/utils/validation.ts
Normal file
@@ -0,0 +1,4 @@
|
||||
export const isValidEmail = (email: string): boolean => {
|
||||
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/
|
||||
return emailRegex.test(email)
|
||||
}
|
||||
Reference in New Issue
Block a user