From e043391c53a287c9ead949e81850fd53354b6cdf Mon Sep 17 00:00:00 2001 From: Lvyshnevska Date: Tue, 1 Oct 2024 18:43:12 +0200 Subject: [PATCH 01/27] route for admin profile --- FrontEnd/src/pages/AdminPage/Menu/Menu.jsx | 5 +++++ FrontEnd/src/routes/AdminRouter.jsx | 4 +++- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/FrontEnd/src/pages/AdminPage/Menu/Menu.jsx b/FrontEnd/src/pages/AdminPage/Menu/Menu.jsx index 59cd7d2f..3f1e6d37 100644 --- a/FrontEnd/src/pages/AdminPage/Menu/Menu.jsx +++ b/FrontEnd/src/pages/AdminPage/Menu/Menu.jsx @@ -29,6 +29,11 @@ const MENU = [ id: 'am5', title: 'Пошта адміністратора', link: '/customadmin/email/' + }, + { + id: 'am6', + title: 'Профіль адміністратора', + link: '/customadmin/admin-profile/' } ]; diff --git a/FrontEnd/src/routes/AdminRouter.jsx b/FrontEnd/src/routes/AdminRouter.jsx index 1237273c..2131b1c2 100644 --- a/FrontEnd/src/routes/AdminRouter.jsx +++ b/FrontEnd/src/routes/AdminRouter.jsx @@ -14,10 +14,11 @@ import Loader from '../components/Loader/Loader'; import AutoApproveDelay from '../pages/AdminPage/AutoApproveDelay/AutoApproveDelay'; import ModerationEmail from '../pages/AdminPage/DetailView/ModerationEmail'; import Contacts from '../pages/AdminPage/DetailView/Contacts'; +import AdminProfilePage from '../pages/AdminPage/AdminProfile/AdminProfilePage'; function AdminRouter() { - const { isLoading, isAuth, isStaff } = useAuth(); + const { isLoading, isAuth, isStaff, user } = useAuth(); const renderMenu = isStaff && isAuth ? : null; const authRoutes = isStaff && isAuth ? ( <> @@ -29,6 +30,7 @@ function AdminRouter() { } /> } /> } /> + } /> ) : ( From 7a0a1831a002b45befb391883e45ca842d767798 Mon Sep 17 00:00:00 2001 From: Lvyshnevska Date: Tue, 1 Oct 2024 18:43:52 +0200 Subject: [PATCH 02/27] admin profile page minor functionality --- .../AdminPage/AdminProfile/AdminInfo.jsx | 124 ++++++ .../AdminProfile/AdminInfo.module.css | 366 ++++++++++++++++++ .../AdminProfile/AdminProfilePage.jsx | 14 + .../AdminProfile/ChangeAdminPassword.jsx | 113 ++++++ .../ChangeAdminPassword.module.css | 5 + 5 files changed, 622 insertions(+) create mode 100644 FrontEnd/src/pages/AdminPage/AdminProfile/AdminInfo.jsx create mode 100644 FrontEnd/src/pages/AdminPage/AdminProfile/AdminInfo.module.css create mode 100644 FrontEnd/src/pages/AdminPage/AdminProfile/AdminProfilePage.jsx create mode 100644 FrontEnd/src/pages/AdminPage/AdminProfile/ChangeAdminPassword.jsx create mode 100644 FrontEnd/src/pages/AdminPage/AdminProfile/ChangeAdminPassword.module.css diff --git a/FrontEnd/src/pages/AdminPage/AdminProfile/AdminInfo.jsx b/FrontEnd/src/pages/AdminPage/AdminProfile/AdminInfo.jsx new file mode 100644 index 00000000..f279fe15 --- /dev/null +++ b/FrontEnd/src/pages/AdminPage/AdminProfile/AdminInfo.jsx @@ -0,0 +1,124 @@ +import { useForm } from 'react-hook-form'; +import classes from './AdminInfo.module.css'; + +const AdminInfo = ({ user }) => { + const { + register, + handleSubmit, + setValue, + getValues, + formState: { errors }, + } = useForm({ + defaultValues: { + 'name': user.name, + 'surname': user.surname, + }, + }); + + const onSubmit = (data) => { + console.log(data); + }; + + const errorMessageTemplates = { + required: 'Обов’язкове поле', + nameSurnameFieldLength: 'Введіть від 2 до 50 символів', + notAllowedSymbols: 'Поле містить недопустимі символи та/або цифри', + maxLength: 'Кількість символів перевищує максимально допустиму (50 символів)', + }; + + const validateNameSurname = (value) => { + const allowedSymbolsPattern = /^[a-zA-Zа-щюяьА-ЩЮЯЬїЇіІєЄґҐ'\s]+$/; + const letterCount = (value.match(/[a-zA-Zа-щюяьА-ЩЮЯЬїЇіІєЄґҐ]/g) || []) + .length; + if (!allowedSymbolsPattern.test(value)) { + return errorMessageTemplates.notAllowedSymbols; + } + if (letterCount < 2) { + return errorMessageTemplates.nameSurnameFieldLength; + } + return true; + }; + + const onBlurHandler = (fieldName) => { + let fieldValue = getValues(fieldName); + if (fieldValue !== undefined && fieldValue !== null) { + fieldValue = fieldValue.replace(/\s{2,}/g, ' ').trim(); + setValue(fieldName, fieldValue); + } + }; + + return ( +
+
+
+
+
+ + +
+
+ onBlurHandler('name')} + /> +
+
+ {errors.name && errors.name.message} +
+
+
+
+ + +
+
+ onBlurHandler('surname')} + /> +
+
+ {errors.surname && errors.surname.message} +
+
+
+ +
+
+
+
+ ); +}; + +export default AdminInfo; \ No newline at end of file diff --git a/FrontEnd/src/pages/AdminPage/AdminProfile/AdminInfo.module.css b/FrontEnd/src/pages/AdminPage/AdminProfile/AdminInfo.module.css new file mode 100644 index 00000000..342348c1 --- /dev/null +++ b/FrontEnd/src/pages/AdminPage/AdminProfile/AdminInfo.module.css @@ -0,0 +1,366 @@ +.admin-info-form { + display: flex; + padding: 24px; +} + +.admin-info-form__container { + display: flex; + flex-direction: row; + align-items: center; + gap: 8px; + flex-wrap: wrap; +} + +.signup-form__row { + display: flex; + align-items: flex-start; + gap: 8px; +} + +.signup-form__column { + display: flex; + width: 257px; + min-height: 84px; + padding-bottom: 0px; + flex-direction: column; + align-items: flex-start; +} + +.signup-form__label { + display: flex; + gap: 4px; + align-self: stretch; + color: var(--main-black-90, #292e32); + font-feature-settings: "calt" off; + font-family: var(--font-main); + font-size: 14px; + font-style: normal; + font-weight: 400; + line-height: 20px; + letter-spacing: -0.14px; + text-align: left; +} + +.signup-form__label--password { + display: flex; + align-self: stretch; + flex-direction: column; + color: var(--main-black-90, #292e32); + font-feature-settings: "calt" off; + text-align: left; + font-family: var(--font-main); + font-size: 14px; + font-style: normal; + font-weight: 400; + line-height: 10px; + letter-spacing: -0.14px; +} + +.signup-form__label--hint { + align-self: stretch; + font-size: 8px; + line-height: 16px; + position: relative; + top: 0px; +} + +.signup-form__label--required { + color: var(--dust-red-5, #ff4d4f); + text-align: right; + font-family: var(--font-messages); + font-size: 14px; + font-style: normal; + font-weight: 400; + line-height: 22.001px; + position: relative; + top: 10%; +} + +.signup-form__label--required_special { + color: var(--dust-red-5, #ff4d4f); + text-align: right; + font-family: var(--font-messages); + font-size: 14px; + font-style: normal; + font-weight: 400; + line-height: 22.001px; + position: relative; + bottom: 10%; +} + +.signup-form__label--text { + display: flex; + flex-direction: column; + justify-content: center; + flex: 1 0 0; + font-family: var(--font-main); + font-size: 14px; + font-weight: 400; + color: var(--main-text-color); +} + +.signup-form__field { + display: flex; + align-items: center; + align-self: stretch; + border-radius: 2px; + background: var(--main-white, #fff); +} + +.signup-form__field__password { + display: flex; + padding-right: 10px; + align-items: center; + gap: 4px; + align-self: stretch; + border-radius: 2px; + border: 1px solid var(--neutral-5, #D9D9D9); + background: var(--main-white, #FFF); +} + +.signup-form__input__password { + font-family: var(--font-messages); + height: 22px; + padding: 5px 12px; + font-size: 14px; + font-style: normal; + font-weight: 400; + display: flex; + align-items: center; + align-self: stretch; + gap: 4px; + flex: 1 0 0; + border: none; +} + +.signup-form__field__password:focus-within { + border: 1px solid var(--main-header-color); +} + +.signup-form__input__password:focus { + outline: none; +} + +.signup-form__input { + display: flex; + height: 22px; + padding: 5px 12px; + align-items: center; + gap: 4px; + flex: 1 0 0; + border-radius: 2px; + border: 1px solid var(--neutral-5, #d9d9d9); + background: var(--main-white, #fff); + color: var(--character-title-85, rgba(0, 0, 0, 0.85)); + font-family: var(--font-messages); + font-size: 14px; + font-style: normal; + font-weight: 400; + line-height: 22px; +} + +.signup-form__input:focus { + border: 1px solid var(--main-header-color); + outline: none; +} + +.signup-form__error { + display: flex; + flex-direction: column; + align-items: flex-start; + align-self: stretch; + color: var(--red-red-100, #f34444); + font-family: var(--font-messages); + font-size: 12px; + font-style: normal; + font-weight: 400; + line-height: 16px; +} + +.signup-form__checkboxes-container { + display: flex; + flex-direction: column; + align-items: flex-start; +} + +.signup-form__checkboxes-container--rules { + display: flex; + width: 328px; + align-items: flex-start; + gap: 8px; +} + +.rules__container { + display: flex; + align-items: center; + gap: 4px; + align-self: stretch; +} + +.rules__line { + display: flex; + align-items: center; + gap: 8px; +} + +.rules__line--text { + color: var(--main-black-90, #292e32); + font-feature-settings: "calt" off; + font-family: var(--font-main); + font-size: 14px; + font-style: normal; + font-weight: 400; + line-height: 20px; + letter-spacing: -0.14px; +} + +.rules__line--link { + color: #00a3cf; + font-feature-settings: "calt" off; + font-family: var(--font-main); + font-size: 14px; + font-style: normal; + font-weight: 400; + line-height: 20px; + letter-spacing: -0.14px; + text-decoration-line: underline; + cursor: pointer; +} + +.rules__checkbox { + display: flex; + flex-direction: column; + justify-content: center; + align-items: center; +} + +.representative { + display: flex; + height: 100px; + flex-direction: column; + align-items: flex-start; + gap: 8px; +} + +.representative__title { + display: flex; + align-items: center; + gap: 4px; + color: var(--main-black-90, #292e32); + font-feature-settings: "calt" off; + font-family: var(--font-main); + font-size: 14px; + font-style: normal; + font-weight: 600; + line-height: 20px; + letter-spacing: -0.14px; +} + +.representative__container { + display: flex; + flex-direction: row; + align-items: flex-start; +} + +.representative__content { + display: flex; + padding: 0px 11px; + align-items: flex-start; + gap: 8px; +} + +.representative__column { + display: flex; + width: 246px; + align-items: flex-start; + gap: 273px; +} + +.representative__option { + display: flex; + align-items: flex-start; + gap: 8px; +} + +.representative__checkbox-container { + display: flex; + flex-direction: column; + justify-content: center; + align-items: center; +} + +.representative__label { + color: var(--main-black-90, #292e32); + font-feature-settings: "calt" off; + font-family: var(--font-main); + font-size: 14px; + font-style: normal; + font-weight: 400; + line-height: 20px; + letter-spacing: -0.14px; + text-align: left; +} + +input[type="checkbox"] { + appearance: none; + background-color: #fff; + margin: 0; + display: grid; + place-content: center; + border-radius: 2px; + border: 1px solid var(--main-black-20, #e2e5eb); +} + +input[type="checkbox"]::before { + content: ""; + width: 16px; + height: 16px; +} + +input[type="checkbox"]:checked { + background: var(--primary-green-80, #1f9a7c); + border-radius: 2px; + border: 1px solid var(--primary-green-80, #1f9a7c); +} + +input[type="checkbox"]:checked::before { + transform: scale(1); + content: "\2713"; + font-size: 17px; + text-align: center; + position: relative; + bottom: 3px; + color: #f0f0f0; +} + +.password-visibility { + cursor: pointer; +} + +.admin-submit__container { + margin-bottom: 6px; + margin-left: 15px; +} + +.admin-submit__button { + display: flex; + border-radius: 4px; + border: 1px solid #1F9A7C; + background: white; + box-shadow: 0px 2px 0px 0px rgba(0, 0, 0, 0.04); + color: #1F9A7C; + font-feature-settings: 'calt' off; + font-style: normal; + padding: 5px 15px 5px 15px; + font-family: var(--font-main); + font-size: 16px; + font-weight: 600; + line-height: 20px; + letter-spacing: -0.01em; + text-align: center; + cursor: pointer; +} + +.admin-submit__button:active { + transform: translateY(2px); +} diff --git a/FrontEnd/src/pages/AdminPage/AdminProfile/AdminProfilePage.jsx b/FrontEnd/src/pages/AdminPage/AdminProfile/AdminProfilePage.jsx new file mode 100644 index 00000000..4c86aaa1 --- /dev/null +++ b/FrontEnd/src/pages/AdminPage/AdminProfile/AdminProfilePage.jsx @@ -0,0 +1,14 @@ +import ChangeAdminPassword from './ChangeAdminPassword'; +import AdminInfo from './AdminInfo'; + +const AdminProfilePage = ({ user }) => { + + return ( +
+ + +
+ ); +}; + +export default AdminProfilePage; \ No newline at end of file diff --git a/FrontEnd/src/pages/AdminPage/AdminProfile/ChangeAdminPassword.jsx b/FrontEnd/src/pages/AdminPage/AdminProfile/ChangeAdminPassword.jsx new file mode 100644 index 00000000..4ebb6f68 --- /dev/null +++ b/FrontEnd/src/pages/AdminPage/AdminProfile/ChangeAdminPassword.jsx @@ -0,0 +1,113 @@ +import axios from 'axios'; +import { PropTypes } from 'prop-types'; +import { toast } from 'react-toastify'; +import { useForm } from 'react-hook-form'; +import PasswordField from '../../ProfilePage/FormComponents/FormFields/PasswordField'; +import Loader from '../../../components/Loader/Loader'; +import classes from './ChangeAdminPassword.module.css'; + +export default function ChangeAdminPassword(props) { + + const { + register, + handleSubmit, + getValues, + watch, + reset, + formState: { errors }, + } = useForm({ + mode: 'all', + defaultValues: { + currentPassword: '', + newPassword: '', + reNewPassword: '', + }, + }); + + 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((error) => { + if (error.response && error.response.data && error.response.data['new_password']) { + const newPasswordError = error.response.data['new_password'][0]; + if (newPasswordError === 'This password is too common.') { + toast.error('Пароль занадто поширений. Створіть інший пароль.'); + } else if (newPasswordError.startsWith('The password is too similar to the')) { + toast.error('Пароль подібний на іншу персональну інформацію облікового запису. Створіть інший пароль.'); + } + } else + toast.error('Виникла помилка. Можливо, вказано невірний поточний пароль'); + }); + reset(); + }; + + return ( +
+ {props.user ? ( +
+ + + + + ) : ( + + )} +
+ ); +} + +ChangeAdminPassword.propTypes = { + user: PropTypes.shape({ + id: PropTypes.number.isRequired, + email: PropTypes.string.isRequired, + name: PropTypes.string.isRequired, + surname: PropTypes.string.isRequired, + is_staff: PropTypes.bool.isRequired, + }).isRequired, +}; diff --git a/FrontEnd/src/pages/AdminPage/AdminProfile/ChangeAdminPassword.module.css b/FrontEnd/src/pages/AdminPage/AdminProfile/ChangeAdminPassword.module.css new file mode 100644 index 00000000..673160d6 --- /dev/null +++ b/FrontEnd/src/pages/AdminPage/AdminProfile/ChangeAdminPassword.module.css @@ -0,0 +1,5 @@ +.form__container { + word-wrap: break-word; + width: 530px; + margin-left: 10px; +} From 4319e17d26b9e89837c666e17ed0e550e6f2825f Mon Sep 17 00:00:00 2001 From: Lvyshnevska Date: Wed, 2 Oct 2024 09:54:36 +0200 Subject: [PATCH 03/27] styles updated --- .../AdminPage/AdminProfile/AdminInfo.jsx | 30 +-- .../AdminProfile/AdminInfo.module.css | 239 +----------------- .../AdminProfile/ChangeAdminPassword.jsx | 95 ++++--- .../ChangeAdminPassword.module.css | 41 ++- 4 files changed, 120 insertions(+), 285 deletions(-) diff --git a/FrontEnd/src/pages/AdminPage/AdminProfile/AdminInfo.jsx b/FrontEnd/src/pages/AdminPage/AdminProfile/AdminInfo.jsx index f279fe15..69f910b1 100644 --- a/FrontEnd/src/pages/AdminPage/AdminProfile/AdminInfo.jsx +++ b/FrontEnd/src/pages/AdminPage/AdminProfile/AdminInfo.jsx @@ -51,16 +51,16 @@ const AdminInfo = ({ user }) => {
-
-
-