Files
bakery-ia/frontend/src/components/ui/PasswordCriteria.tsx

131 lines
3.3 KiB
TypeScript
Raw Normal View History

import React from 'react';
interface PasswordCriteria {
label: string;
isValid: boolean;
regex?: RegExp;
checkFn?: (password: string) => boolean;
}
interface PasswordCriteriaProps {
password: string;
className?: string;
showOnlyFailed?: boolean;
}
export const PasswordCriteria: React.FC<PasswordCriteriaProps> = ({
password,
className = '',
showOnlyFailed = false
}) => {
const criteria: PasswordCriteria[] = [
{
label: 'Al menos 8 caracteres',
isValid: password.length >= 8,
checkFn: (pwd) => pwd.length >= 8
},
{
label: 'Máximo 128 caracteres',
isValid: password.length <= 128,
checkFn: (pwd) => pwd.length <= 128
},
{
label: 'Al menos una letra mayúscula',
isValid: /[A-Z]/.test(password),
regex: /[A-Z]/
},
{
label: 'Al menos una letra minúscula',
isValid: /[a-z]/.test(password),
regex: /[a-z]/
},
{
label: 'Al menos un número',
isValid: /\d/.test(password),
regex: /\d/
}
];
const validatedCriteria = criteria.map(criterion => ({
...criterion,
isValid: criterion.regex
? criterion.regex.test(password)
: criterion.checkFn
? criterion.checkFn(password)
: false
}));
const displayCriteria = showOnlyFailed
? validatedCriteria.filter(c => !c.isValid)
: validatedCriteria;
if (displayCriteria.length === 0) return null;
return (
<div className={`text-sm space-y-1 ${className}`}>
<p className="text-text-secondary font-medium mb-2">
Requisitos de contraseña:
</p>
<ul className="space-y-1">
{displayCriteria.map((criterion, index) => (
<li key={index} className="flex items-center space-x-2">
<span
className={`w-4 h-4 rounded-full flex items-center justify-center text-xs font-bold ${
criterion.isValid
? 'bg-green-100 text-green-700 dark:bg-green-900 dark:text-green-300'
: 'bg-red-100 text-red-700 dark:bg-red-900 dark:text-red-300'
}`}
>
{criterion.isValid ? '✓' : '✗'}
</span>
<span
className={`${
criterion.isValid
? 'text-green-700 dark:text-green-300'
: 'text-red-700 dark:text-red-300'
}`}
>
{criterion.label}
</span>
</li>
))}
</ul>
</div>
);
};
export const validatePassword = (password: string): boolean => {
return (
password.length >= 8 &&
password.length <= 128 &&
/[A-Z]/.test(password) &&
/[a-z]/.test(password) &&
/\d/.test(password)
);
};
export const getPasswordErrors = (password: string): string[] => {
const errors: string[] = [];
if (password.length < 8) {
errors.push('La contraseña debe tener al menos 8 caracteres');
}
if (password.length > 128) {
errors.push('La contraseña no puede exceder 128 caracteres');
}
if (!/[A-Z]/.test(password)) {
errors.push('La contraseña debe contener al menos una letra mayúscula');
}
if (!/[a-z]/.test(password)) {
errors.push('La contraseña debe contener al menos una letra minúscula');
}
if (!/\d/.test(password)) {
errors.push('La contraseña debe contener al menos un número');
}
return errors;
};