Press n or j to go to the next uncovered block, b, p or k for the previous block.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 | 12x 12x 12x 12x 12x 12x 12x 1x 1x 60x 1x 1x | import { useState, useMemo } from 'react'; import TextField from '@mui/material/TextField'; import Box from '@mui/material/Box'; import Typography from '@mui/material/Typography'; import InputAdornment from '@mui/material/InputAdornment'; import IconButton from '@mui/material/IconButton'; import CheckCircleIcon from '@mui/icons-material/CheckCircle'; import RadioButtonUncheckedIcon from '@mui/icons-material/RadioButtonUnchecked'; import Visibility from '@mui/icons-material/Visibility'; import VisibilityOff from '@mui/icons-material/VisibilityOff'; import { useTranslation } from 'react-i18next'; export interface PasswordWithConfirmationProps { password: string; onPasswordChange: (pw: string) => void; confirmPassword: string; onConfirmChange: (pw: string) => void; touched: boolean; onBlur: () => void; } export default function PasswordWithConfirmation({ password, onPasswordChange, confirmPassword, onConfirmChange, touched, onBlur, }: PasswordWithConfirmationProps) { const { t } = useTranslation('signup'); const [showPassword, setShowPassword] = useState(false); const [showConfirm, setShowConfirm] = useState(false); // critères de robustesse const criteria = useMemo(() => ({ length: password.length >= 15, upper: /[A-Z]/.test(password), lower: /[a-z]/.test(password), number: /\d/.test(password), special:/[\W_]/.test(password), }), [password]); const pwStrong = Object.values(criteria).every(Boolean); const pwsMatch = password === confirmPassword; return ( <Box> {/* Mot de passe */} <TextField required fullWidth type={showPassword ? 'text' : 'password'} label={t('signup.passwordLabel')} value={password} onChange={e => onPasswordChange(e.target.value)} onBlur={onBlur} autoComplete="new-password" error={touched && !pwStrong} helperText={ touched && !pwStrong ? t('signup.hintPasswordCriteria') : '' } slotProps={{ input: { endAdornment: ( <InputAdornment position="end"> <IconButton aria-label={showPassword ? t('signup.hidePassword') : t('signup.showPassword')} onClick={() => setShowPassword(!showPassword)} edge="end" > {showPassword ? <VisibilityOff /> : <Visibility />} </IconButton> </InputAdornment> ), }, }} /> {/* Checklist de critères */} <Box sx={{ pl: 1, mt: 1 }}> {Object.entries(criteria).map(([key, ok]) => ( <Box key={key} sx={{ display: 'flex', alignItems: 'center', mb: 0.5 }} > {ok ? <CheckCircleIcon fontSize="small" sx={{ color: 'success.main', mr: 1 }}/> : <RadioButtonUncheckedIcon fontSize="small" sx={{ color: 'text.disabled', mr: 1 }}/> } <Typography variant="body2"> {t(`passwordCriteria.${key}`)} </Typography> </Box> ))} </Box> {/* Confirmation */} <TextField required fullWidth sx={{ mt: 2 }} type={showConfirm ? 'text' : 'password'} label={t('signup.confirmPasswordLabel')} value={confirmPassword} onChange={e => onConfirmChange(e.target.value)} onBlur={onBlur} autoComplete="new-password" error={touched && !pwsMatch} helperText={ touched && !pwsMatch ? t('signup.hintPasswordsDontMatch') : '' } slotProps={{ input: { endAdornment: ( <InputAdornment position="end"> <IconButton aria-label={showConfirm ? t('signup.hidePassword') : t('signup.showPassword')} onClick={() => setShowConfirm(!showConfirm)} edge="end" > {showConfirm ? <VisibilityOff /> : <Visibility />} </IconButton> </InputAdornment> ), }, }} /> </Box> ); } |