chore: import codes
This commit is contained in:
@@ -0,0 +1,189 @@
|
||||
/**
|
||||
* Password Strength Indicator Component
|
||||
* Shows password strength and validation requirements
|
||||
*/
|
||||
|
||||
'use client';
|
||||
|
||||
import { useState, useEffect } from 'react';
|
||||
|
||||
export default function PasswordStrengthIndicator({ password = '', showRequirements = true }) {
|
||||
const [strength, setStrength] = useState(0);
|
||||
const [requirements, setRequirements] = useState({
|
||||
length: false,
|
||||
maxLength: false,
|
||||
uppercase: false,
|
||||
lowercase: false,
|
||||
number: false
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
if (!password) {
|
||||
setStrength(0);
|
||||
setRequirements({
|
||||
length: false,
|
||||
uppercase: false,
|
||||
lowercase: false,
|
||||
number: false
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
const newRequirements = {
|
||||
length: password.length >= 8,
|
||||
maxLength: password.length <= 128,
|
||||
uppercase: /[A-Z]/.test(password),
|
||||
lowercase: /[a-z]/.test(password),
|
||||
number: /\d/.test(password)
|
||||
};
|
||||
|
||||
setRequirements(newRequirements);
|
||||
|
||||
// Calculate strength based on both requirements and length
|
||||
const requirementsMet = Object.values(newRequirements).filter(Boolean).length;
|
||||
let strengthValue = requirementsMet;
|
||||
|
||||
// Adjust strength based on password length
|
||||
if (password.length >= 12) {
|
||||
strengthValue = Math.min(5, strengthValue + 1); // Bonus for long passwords
|
||||
} else if (password.length >= 8 && password.length < 10) {
|
||||
strengthValue = Math.max(1, strengthValue - 1); // Penalty for short passwords
|
||||
} else if (password.length < 8) {
|
||||
strengthValue = Math.max(0, strengthValue - 2); // Big penalty for very short passwords
|
||||
}
|
||||
|
||||
// Ensure strength is between 0 and 5
|
||||
strengthValue = Math.max(0, Math.min(5, strengthValue));
|
||||
|
||||
setStrength(strengthValue);
|
||||
}, [password]);
|
||||
|
||||
const getStrengthColor = () => {
|
||||
switch (strength) {
|
||||
case 0:
|
||||
case 1:
|
||||
return 'bg-red-500';
|
||||
case 2:
|
||||
return 'bg-orange-500';
|
||||
case 3:
|
||||
return 'bg-yellow-500';
|
||||
case 4:
|
||||
return 'bg-blue-500';
|
||||
case 5:
|
||||
return 'bg-green-500';
|
||||
default:
|
||||
return 'bg-gray-500';
|
||||
}
|
||||
};
|
||||
|
||||
const getStrengthText = () => {
|
||||
switch (strength) {
|
||||
case 0:
|
||||
return 'Très faible';
|
||||
case 1:
|
||||
return 'Faible';
|
||||
case 2:
|
||||
return 'Moyen';
|
||||
case 3:
|
||||
return 'Bon';
|
||||
case 4:
|
||||
return 'Très bon';
|
||||
case 5:
|
||||
return 'Excellent';
|
||||
default:
|
||||
return '';
|
||||
}
|
||||
};
|
||||
|
||||
const getStrengthTextColor = () => {
|
||||
switch (strength) {
|
||||
case 0:
|
||||
case 1:
|
||||
return 'text-red-700 dark:text-red-400';
|
||||
case 2:
|
||||
return 'text-orange-700 dark:text-orange-400';
|
||||
case 3:
|
||||
return 'text-yellow-700 dark:text-yellow-500';
|
||||
case 4:
|
||||
return 'text-blue-700 dark:text-blue-400';
|
||||
case 5:
|
||||
return 'text-green-700 dark:text-green-400';
|
||||
default:
|
||||
return 'text-neutral-600 dark:text-neutral-400';
|
||||
}
|
||||
};
|
||||
|
||||
if (!password && !showRequirements) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="mt-2 space-y-2">
|
||||
{/* Strength Bar */}
|
||||
{password && (
|
||||
<div className="space-y-1">
|
||||
<div className="flex items-center justify-between">
|
||||
<span className="text-xs text-neutral-600 dark:text-neutral-400">Force du mot de passe :</span>
|
||||
<span className={`text-xs font-medium ${getStrengthTextColor()}`}>
|
||||
{getStrengthText()}
|
||||
</span>
|
||||
</div>
|
||||
<div className="w-full bg-neutral-200 dark:bg-neutral-700 rounded-full h-1.5">
|
||||
<div
|
||||
className={`h-1.5 rounded-full transition-all duration-300 ${getStrengthColor()}`}
|
||||
style={{ width: `${(strength / 5) * 100}%` }}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Requirements - Only show if not all requirements are met */}
|
||||
{showRequirements && password && !Object.values(requirements).every(Boolean) && (
|
||||
<div className="space-y-1">
|
||||
<div className="space-y-1">
|
||||
{!requirements.length && (
|
||||
<div className="flex items-center space-x-2">
|
||||
<div className="w-1.5 h-1.5 rounded-full flex-shrink-0 bg-red-500" />
|
||||
<span className="text-xs text-red-700 dark:text-red-400">
|
||||
Au moins 8 caractères
|
||||
</span>
|
||||
</div>
|
||||
)}
|
||||
{!requirements.maxLength && (
|
||||
<div className="flex items-center space-x-2">
|
||||
<div className="w-1.5 h-1.5 rounded-full flex-shrink-0 bg-red-500" />
|
||||
<span className="text-xs text-red-700 dark:text-red-400">
|
||||
Maximum 128 caractères
|
||||
</span>
|
||||
</div>
|
||||
)}
|
||||
{!requirements.uppercase && (
|
||||
<div className="flex items-center space-x-2">
|
||||
<div className="w-1.5 h-1.5 rounded-full flex-shrink-0 bg-red-500" />
|
||||
<span className="text-xs text-red-700 dark:text-red-400">
|
||||
Au moins une majuscule
|
||||
</span>
|
||||
</div>
|
||||
)}
|
||||
{!requirements.lowercase && (
|
||||
<div className="flex items-center space-x-2">
|
||||
<div className="w-1.5 h-1.5 rounded-full flex-shrink-0 bg-red-500" />
|
||||
<span className="text-xs text-red-700 dark:text-red-400">
|
||||
Au moins une minuscule
|
||||
</span>
|
||||
</div>
|
||||
)}
|
||||
{!requirements.number && (
|
||||
<div className="flex items-center space-x-2">
|
||||
<div className="w-1.5 h-1.5 rounded-full flex-shrink-0 bg-red-500" />
|
||||
<span className="text-xs text-red-700 dark:text-red-400">
|
||||
Au moins un chiffre
|
||||
</span>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user