Skip to content

Commit

Permalink
Merge pull request #527 from ita-social-projects/525-implement-abilit…
Browse files Browse the repository at this point in the history
…y-to-change-password

525 implement ability to change password
  • Loading branch information
YanZhylavy authored Apr 19, 2024
2 parents fd6f011 + 029340d commit d3829c5
Show file tree
Hide file tree
Showing 6 changed files with 330 additions and 1 deletion.
125 changes: 125 additions & 0 deletions FrontEnd/src/components/ProfilePage/FormComponents/ChangePassword.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
import axios from 'axios';
import { PropTypes } from 'prop-types';
import { toast } from 'react-toastify';
import { useEffect } from 'react';
import { useContext } from 'react';
import { useForm } from 'react-hook-form';
import { DirtyFormContext } from '../../../context/DirtyFormContext';
import PasswordField from './FormFields/PasswordField';
import Loader from '../../loader/Loader';
import css from './ChangePassword.module.css';


export default function ChangePassword(props) {
const { setFormIsDirty } = useContext(DirtyFormContext);
const { currentFormNameHandler, curForm } = props;
const {
register,
handleSubmit,
getValues,
watch,
reset,
formState: { errors, isDirty },
} = useForm({
mode: 'all',
defaultValues: {
currentPassword: '',
newPassword: '',
reNewPassword: ''
}
});

useEffect(() => {
currentFormNameHandler(curForm);
}, [currentFormNameHandler, curForm]);

useEffect(() => {
setFormIsDirty(isDirty);
}, [isDirty, setFormIsDirty]
);

const handleFormSubmit = () => {
axios.post(`${process.env.REACT_APP_BASE_API_URL}/api/auth/users/set_password/`, {
current_password: getValues('currentPassword'),
new_password: getValues('newPassword'),
re_new_password: getValues('reNewPassword')
})
.then(() => toast.success('Пароль успішно змінено'))
.catch(() => toast.error('Виникла помилка. Можливо, вказано невірний поточний пароль'));
reset();
};

return (
<div className={css['form__container']}>
{props.user
?
<form id="ChangePassword" onSubmit={handleSubmit(handleFormSubmit)}>
<PasswordField
inputId="currentPassword"
name="currentPassword"
label="Поточний пароль"
register={register}
error={errors}
showError={false}
watch={watch}
checkValid={false}
checkMatch={
{
isCheck: false,
checkWith: null
}
}
/>
<PasswordField
inputId="newPassword"
name="newPassword"
label="Новий пароль"
error={errors}
register={register}
showError={true}
watch={watch}
checkValid={true}
checkMatch={
{
isCheck: false,
checkWith: null
}
}
/>
<PasswordField
inputId="reNewPassword"
name="reNewPassword"
label="Повторіть новий пароль"
error={errors}
register={register}
showError={true}
watch={watch}
checkValid={false}
checkMatch={
{
isCheck: true,
checkWith: 'newPassword'
}
}
/>
</form>
: <Loader />
}
</div>
);
}

ChangePassword.propTypes = {
user: PropTypes.shape({
id: PropTypes.number.isRequired,
email: PropTypes.string.isRequired,
name: PropTypes.string.isRequired,
surname: PropTypes.string.isRequired,
profile_id: PropTypes.number.isRequired,
is_staff: PropTypes.bool.isRequired
}).isRequired,
currentFormNameHandler: PropTypes.func.isRequired,
curForm: PropTypes.string.isRequired
};


Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
.form__container {
word-wrap: break-word;
width: 530px;
margin-left: 10px;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
import { useState } from 'react';
import { PropTypes } from 'prop-types';
import EyeInvisible from '../../../authorization/EyeInvisible';
import EyeVisible from '../../../authorization/EyeVisible';
import css from './PasswordField.module.css';
import { PASSWORD_PATTERN } from '../../../../constants/constants';

const PasswordField = (props) => {

const errorMessages = {
invalidPassword: 'Пароль не відповідає вимогам',
passwordsDontMatch: 'Паролі не співпадають'
};

const [showPassword, setShowPassword] = useState(false);

const togglePassword = () => {
setShowPassword(!showPassword);
};

const {
register,
name,
error,
showError,
watch,
label,
inputId,
checkValid,
checkMatch
} = props;

return (
<div className={css['password-field__item']}>
<div className={css['password-field__label-wrapper']}>
<span>
*
</span>
<label
htmlFor={inputId}
>
{label}
</label>
</div>
<div className={css['password-field__password']}>
<div className={css['password-field__password__wrapper']}>
<input
id={inputId}
type={showPassword ? 'text' : 'password'}
placeholder={label}
required
{...register(name,
{
pattern: checkValid && {
value: PASSWORD_PATTERN,
message: errorMessages.invalidPassword
},
validate: checkMatch.isCheck ?
(value) => {
return value === watch(checkMatch.checkWith) ||
value === '' ||
errorMessages.passwordsDontMatch;
} :
null
})}
/>
</div>
<span
aria-label="Toggle password visibility"
className={css['password-visibility']}
onClick={togglePassword}
>
{!showPassword ? <EyeInvisible /> : <EyeVisible />}
</span>
</div>
{(error[name] && showError) ?
<div className={css['error-message']}>
{error[name].message}
</div>
:
null
}
</div>
);
};

PasswordField.propTypes = {
name: PropTypes.string.isRequired,
label: PropTypes.string.isRequired,
register: PropTypes.func.isRequired,
watch: PropTypes.func.isRequired,
inputId: PropTypes.string.isRequired,
showError: PropTypes.bool.isRequired,
error: PropTypes.object,
checkValid: PropTypes.bool.isRequired,
checkMatch: PropTypes.shape({
isCheck: PropTypes.bool.isRequired,
checkWith: PropTypes.string
})
};

export default PasswordField;
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
.password-field__item {
display: flex;
width: 257px;
height: 89px;
flex-direction: column;
align-items: flex-start;
}

.password-field__label-wrapper {
padding-bottom: 9px;
}

.password-field__label-wrapper span {
padding-right: 5px;
color: #FF4D4F;
}

.password-field__item input {
border: none;
}
.password-field__item input:focus {
border: none;
outline: none;
}

.password-field__item input::placeholder {
font-style: normal;
font-family: "Inter", sans-serif;
font-size: 14px;
font-weight: 400;
line-height: 22px;
letter-spacing: -0.01em;
text-align: left;
color: #00000040;
}
.password-field__password {
display: flex;
padding: 4px 12px;
align-items: center;
gap: 4px;
align-self: stretch;
border-radius: 2px;
border: 1px solid #d9d9d9;
background: #fff;
}

.password-field__password:focus {
border-radius: 2px;
border: 1px solid #1f9a7c;
background: #fff;
outline: none;
}

.password-field__password:focus-within {
border-radius: 2px;
border: 1px solid #1f9a7c;
background: #fff;
}

.password-field__password__wrapper {
display: flex;
}

.password-visibility {
cursor: pointer;
margin-left: auto;
}

.error-message {
display: flex;
padding: 1px 0px;
align-items: flex-start;
gap: 10px;
align-self: stretch;
flex: 1 0 0;
color: var(--red-red-100, #F34444);
font-family: Roboto;
font-size: 14px;
font-style: normal;
font-weight: 400;
line-height: 22px;
}

Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ const DESCRIPTIONS = {
'AdditionalInfo': 'Інформація про компанію',
'StartupInfo': 'Інформація про стартап',
'Delete': 'Видалення профілю',
'ChangePassword': 'Зміна паролю користувача'
};

const Description = (props) => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import UserInfo from '../FormComponents/UserInfo';
import ProfileFormButton from '../UI/ProfileFormButton/ProfileFormButton';
import MyModal from '../UI/MyModal/MyModal';
import WarnUnsavedDataModal from '../FormComponents/WarnUnsavedDataModal';
import ChangePassword from '../FormComponents/ChangePassword';
import css from './ProfileContent.module.css';


Expand Down Expand Up @@ -53,7 +54,8 @@ const FORM_NAMES = [
'ProductServiceInfo',
'AdditionalInfo',
'StartupInfo',
'Delete'
'Delete',
'ChangePassword'
];

const ProfileContent = (props) => {
Expand Down Expand Up @@ -143,6 +145,11 @@ const ProfileContent = (props) => {
);
})}
<div className={css['divider']}></div>
<Link
to="/profile/change-password"
className={`${css['infolink']}`}>
Змінити пароль
</Link>
<Link
to="/profile/delete"
className={`${css['infolink']} ${css['delete']}`}>
Expand Down Expand Up @@ -191,6 +198,12 @@ const ProfileContent = (props) => {
profile={props.profile}
currentFormNameHandler={props.currentFormNameHandler}
curForm={FORM_NAMES[5]} />} />
<Route
path="/change-password"
element={<ChangePassword
user={props.user}
currentFormNameHandler={props.currentFormNameHandler}
curForm={FORM_NAMES[7]} />} />
</Routes>
</DirtyFormContext.Provider>
</div>
Expand Down

0 comments on commit d3829c5

Please sign in to comment.