Start integrating the onboarding flow with backend 1
This commit is contained in:
131
frontend/src/components/ui/PasswordCriteria.tsx
Normal file
131
frontend/src/components/ui/PasswordCriteria.tsx
Normal file
@@ -0,0 +1,131 @@
|
||||
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;
|
||||
};
|
||||
Reference in New Issue
Block a user