Files
Account/src/features/profile/components/security/UserSecurity.tsx

325 lines
10 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import {
Box,
Typography,
Button,
Dialog,
DialogTitle,
DialogContent,
TextField,
DialogActions,
IconButton,
} from '@mui/material';
import { useTranslation } from 'react-i18next';
import { useState, useEffect } from 'react';
import { CloseCircle, Refresh } from 'iconsax-react';
import { PasswordValidationItem } from './PasswordValidation';
import { Toast } from '@/components/Toast';
import Logo from '@/components/Logo';
import { CardContainer } from '@/components/CardContainer';
export function UserSecurity() {
const { t } = useTranslation('security');
const [open, setOpen] = useState(false);
const [password, setPassword] = useState('');
const [confirmPassword, setConfirmPassword] = useState('');
const [showValidation, setShowValidation] = useState(false);
const [showPasswordAlert, setShowPasswordAlert] = useState(false);
const [changePassword, setChangePassword] = useState(false);
const [loading, setLoading] = useState(false);
const hasNumber = /\d/.test(password);
const hasMinLength = password.length >= 8;
const hasUpperAndLower = /[A-Z]/.test(password) && /[a-z]/.test(password);
const hasSpecialChar = /[!@#$%^&*]/.test(password);
const validPassword =
hasNumber && hasMinLength && hasUpperAndLower && hasSpecialChar;
const matchPassword = password === confirmPassword;
const handleOpen = () => setOpen(true);
const handleClose = () => setOpen(false);
const handleShowAlert = () => {
setLoading(true);
setTimeout(() => {
setLoading(false);
setShowPasswordAlert(true);
setChangePassword(true);
handleClose();
setPassword('');
setConfirmPassword('');
}, 1500);
};
useEffect(() => {
if (password) {
if (!validPassword) {
setShowValidation(true);
} else {
const timer = setTimeout(() => setShowValidation(false), 1000);
return () => clearTimeout(timer);
}
} else {
setShowValidation(false);
}
}, [password, validPassword]);
return (
<Box sx={{ overflowX: 'hidden' }}>
<Box
sx={{
backgroundColor: 'background.paper',
width: '100%',
maxWidth: '796px',
mx: 'auto',
px: { xs: 2, sm: 3 },
}}
>
<Box
sx={{
display: 'flex',
justifyContent: 'flex-start',
alignItems: 'center',
py: 2,
// width: '796px',
}}
>
<Logo />
</Box>
<Box
sx={{
// mt: 2,
width: '100%',
height: '1px',
backgroundColor: 'divider',
}}
/>
<Box
sx={{
justifyContent: 'center',
alignItems: 'center',
backgroundColor: 'background.paper',
px: 2,
}}
>
<CardContainer
title={t('securityForm.password')}
subtitle={t('securityForm.determinePassword')}
action={
<Button
variant="outlined"
onClick={handleOpen}
sx={{
mt: { xs: 2, sm: 0 },
backgroundColor: 'primary.main',
color: 'background.paper',
width: '142px',
textTransform: 'none',
}}
>
{t('securityForm.addPassword')}
</Button>
}
>
<Box sx={{ height: '111px' }}>
{changePassword ? (
<Box sx={{ flexDirection: 'column', px: 4, py: 4 }}>
<Typography variant="h6">رمز عبور فعال است</Typography>
<Typography variant="caption" color="text.secondary">
آخرین تغییر چند ثانیه پیش
</Typography>
</Box>
) : (
<Box
sx={{
width: '100%',
maxWidth: '754px',
mx: 'auto',
}}
>
<Typography
variant="body1"
color="text.secondary"
sx={{ textAlign: 'center', py: '43.5px' }}
>
{t('securityForm.notDeterminedPassword')}
</Typography>
</Box>
)}
</Box>
<Dialog
open={open}
onClose={handleClose}
fullWidth
maxWidth="xs"
scroll="body"
PaperProps={{
sx: { mx: 1 },
}}
>
<DialogTitle sx={{ p: 2 }}>
<Box
sx={{
display: 'flex',
flexDirection: 'row',
alignItems: 'center',
gap: 1,
}}
>
<IconButton onClick={handleClose}>
<CloseCircle size={24} color="#82B1FF" />
</IconButton>
<Typography variant="h6" fontWeight="bold">
{t('securityForm.addPassword')}
</Typography>
</Box>
</DialogTitle>
<DialogContent
sx={{
display: 'flex',
flexDirection: 'column',
gap: 2,
px: { xs: 2, sm: 3 },
}}
>
<Box
sx={{
width: '100%',
maxWidth: '364px',
mx: 'auto',
mt: '32px',
}}
>
<TextField
label={t('securityForm.newPassword')}
type="password"
fullWidth
value={password}
onChange={(e) => setPassword(e.target.value)}
sx={{
'& .MuiOutlinedInput-root': {
borderRadius: 2,
},
}}
/>
</Box>
{password && showValidation && (
<Box
sx={{
width: '100%',
maxWidth: '364px',
mx: 'auto',
display: 'flex',
justifyContent: 'flex-end',
mb: '32px',
}}
>
<Box
sx={{
display: 'flex',
flexDirection: 'column',
alignItems: 'flex-start',
textAlign: 'left',
width: '100%',
}}
>
<PasswordValidationItem
isValid={hasNumber}
label={t('securityForm.hasNumber')}
/>
<PasswordValidationItem
isValid={hasMinLength}
label={t('securityForm.hasMinLength')}
/>
<PasswordValidationItem
isValid={hasUpperAndLower}
label={t('securityForm.hasUpperAndLower')}
/>
<PasswordValidationItem
isValid={hasSpecialChar}
label={t('securityForm.hasSpecialChar')}
/>
</Box>
</Box>
)}
<Box
sx={{
width: '100%',
maxWidth: '364px',
mx: 'auto',
}}
>
<TextField
label={t('securityForm.confirmPassword')}
type="password"
fullWidth
value={confirmPassword}
onChange={(e) => setConfirmPassword(e.target.value)}
error={confirmPassword.length > 0 && !matchPassword}
helperText={
confirmPassword.length > 0 && !matchPassword
? t('securityForm.notCompatibility')
: ' '
}
sx={{
'& .MuiOutlinedInput-root': {
borderRadius: 2,
},
}}
/>
</Box>
</DialogContent>
<DialogActions
sx={{
px: 3,
pb: 2,
justifyContent: 'center',
alignItems: 'center',
}}
>
<Button
sx={{ width: '364px', height: 48 }}
variant="contained"
onClick={handleShowAlert}
disabled={
!password || password !== confirmPassword || loading
}
>
{loading ? (
<Box
component="span"
sx={{
display: 'flex',
animation: 'spin 1s linear infinite',
'@keyframes spin': {
'0%': { transform: 'rotate(0deg)' },
'100%': { transform: 'rotate(360deg)' },
},
}}
>
<Refresh size="20" color="#fff" />
</Box>
) : (
t('securityForm.confirm')
)}
</Button>
</DialogActions>
</Dialog>
<Toast
color="success"
open={showPasswordAlert}
onClose={() => setShowPasswordAlert(false)}
>
{t('securityForm.alertSuccess')}
</Toast>
</CardContainer>
</Box>
</Box>
</Box>
);
}