diff --git a/FrontEnd/src/components/BreadCrumbs/BreadCrumbs.jsx b/FrontEnd/src/components/BreadCrumbs/BreadCrumbs.jsx index 25437e2cf..a18553893 100644 --- a/FrontEnd/src/components/BreadCrumbs/BreadCrumbs.jsx +++ b/FrontEnd/src/components/BreadCrumbs/BreadCrumbs.jsx @@ -1,25 +1,37 @@ -import css from './BreadCrumbs.module.css'; import { Link, useNavigate } from 'react-router-dom'; import { PropTypes } from 'prop-types'; +import { useAuth } from '../../hooks'; + +import css from './BreadCrumbs.module.css'; + const BreadCrumbs = ({ currentPage }) => { const navigate = useNavigate(); - + const { isStaff } = useAuth(); const goBackHandler = () => { navigate(-1); }; return (
- - - Головна - + { !isStaff ? ( + + ) : null + } + {!isStaff ? ( + + Головна + + ) : ( + + Панель адміністратора + ) + }
{currentPage}
diff --git a/FrontEnd/src/components/Header/Header.jsx b/FrontEnd/src/components/Header/Header.jsx index 291c665e8..8cb288f19 100644 --- a/FrontEnd/src/components/Header/Header.jsx +++ b/FrontEnd/src/components/Header/Header.jsx @@ -1,16 +1,22 @@ -import css from './Header.module.css'; +import { useLocation } from 'react-router-dom'; + import HeaderMenu from './Menu/HeaderMenu'; import Navbar from './Navbar/Navbar'; +import css from './Header.module.css'; + function Header(props) { + const { pathname } = useLocation(); + const hideMenu = pathname === '/login' || pathname === '/sign-up' || pathname.includes('/customadmin'); + return (
- +
- {props.page === 'login' || props.page === 'registration' ? null : ( - - )} + {hideMenu ? null : ( + + )}
diff --git a/FrontEnd/src/components/Header/Navbar/Buttons.jsx b/FrontEnd/src/components/Header/Navbar/Buttons.jsx index e7b17e88b..86abcab12 100644 --- a/FrontEnd/src/components/Header/Navbar/Buttons.jsx +++ b/FrontEnd/src/components/Header/Navbar/Buttons.jsx @@ -1,11 +1,11 @@ import css from './Buttons.module.css'; import { Link } from 'react-router-dom'; -function Buttons() { +function Buttons({adminPage}) { return (
Увійти - Зареєструватися + {!adminPage && Зареєструватися}
); } diff --git a/FrontEnd/src/components/Header/Navbar/Navbar.jsx b/FrontEnd/src/components/Header/Navbar/Navbar.jsx index c330caf6f..55028c42d 100644 --- a/FrontEnd/src/components/Header/Navbar/Navbar.jsx +++ b/FrontEnd/src/components/Header/Navbar/Navbar.jsx @@ -1,15 +1,21 @@ -import css from './Navbar.module.css'; +import { Link } from 'react-router-dom'; + +import { useAuth } from '../../../hooks'; + import Menu from './Menu'; import SearchBox from './SearchBox'; import Profile from './Profile'; import Buttons from './Buttons'; -import { Link } from 'react-router-dom'; -function Navbar(props) { +import css from './Navbar.module.css'; + +function Navbar (props) { + const { isStaff} = useAuth(); + const hideMenu = props.page === '/login' || props.page === '/sign-up' || props.page.includes('/customadmin'); return (
- +
- {props.page === 'login' || props.page === 'registration' ? null : ( + { hideMenu ? null : ( <> )} - {props.isAuthorized === true ? : } + {props.isAuthorized === true ? : }
); diff --git a/FrontEnd/src/components/Header/Navbar/Navbar.module.css b/FrontEnd/src/components/Header/Navbar/Navbar.module.css index 2a44534c1..c01dbf1e1 100644 --- a/FrontEnd/src/components/Header/Navbar/Navbar.module.css +++ b/FrontEnd/src/components/Header/Navbar/Navbar.module.css @@ -1,16 +1,17 @@ .navbar-content { display: flex; + width: 70%; padding: 8px 104px; align-items: center; justify-content: space-between; height: var(--navbar-content-height); } -.navbar-logo__text{ +.navbar-logo__text { display: flex; } -.navbar-main-logo{ +.navbar-main-logo { display: flex; width: 199px; height: 24px; diff --git a/FrontEnd/src/components/Header/Navbar/Profile.jsx b/FrontEnd/src/components/Header/Navbar/Profile.jsx index c8b58684b..e945f38ca 100644 --- a/FrontEnd/src/components/Header/Navbar/Profile.jsx +++ b/FrontEnd/src/components/Header/Navbar/Profile.jsx @@ -7,7 +7,7 @@ import css from './Profile.module.css'; function Profile() { - const { user, isAuth, logout } = useAuth(); + const { user, isAuth, logout, isStaff } = useAuth(); const navigate = useNavigate(); const navigateToProfile = () => { @@ -31,12 +31,12 @@ function Profile() { className={css['header-profile__avatar']} src={`${process.env.REACT_APP_PUBLIC_URL}/img/Avatar.png`} alt="Avatar" - onClick={navigateToProfile} + onClick={!isStaff ? navigateToProfile : null} /> - - Профіль - - + + Профіль + + ); } diff --git a/FrontEnd/src/components/Header/Navbar/SearchBox.jsx b/FrontEnd/src/components/Header/Navbar/SearchBox.jsx index 569232de6..f27e41262 100644 --- a/FrontEnd/src/components/Header/Navbar/SearchBox.jsx +++ b/FrontEnd/src/components/Header/Navbar/SearchBox.jsx @@ -19,6 +19,7 @@ function SearchBox() {
{ + return ( +
+ +
+ ); +}; + +export default AdminSubmitButton; \ No newline at end of file diff --git a/FrontEnd/src/components/MiniComponents/AdminSubmitButton.module.css b/FrontEnd/src/components/MiniComponents/AdminSubmitButton.module.css new file mode 100644 index 000000000..c25fea2fe --- /dev/null +++ b/FrontEnd/src/components/MiniComponents/AdminSubmitButton.module.css @@ -0,0 +1,27 @@ +.admin-submit__container { + margin-top: 16px; +} + +.admin-submit__button { + display: block; + width: 100%; + border-radius: 4px; + border: 1px solid var(--main-button-color); + background: var(--main-button-color); + box-shadow: 0px 2px 0px 0px rgba(0, 0, 0, 0.04); + color: var(--light-button-text-color); + 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/AdminGlobal.css b/FrontEnd/src/pages/AdminPage/AdminGlobal.css index e717c8da6..ec7e45384 100644 --- a/FrontEnd/src/pages/AdminPage/AdminGlobal.css +++ b/FrontEnd/src/pages/AdminPage/AdminGlobal.css @@ -1,10 +1,13 @@ :root { --font__admin-panel: "Inter", sans-serif; - --block-size__admin-panel: 1500px; - --menu-size__admin-panel: 120px; + --block-size__admin-panel: 1512px; + --menu-size__admin-panel: 222px; --table-size__admin-panel: 1000px; --font-color__admin-panel: #292E32; + --notification-text-color__admin-panel: #ff4d4f; --background__admin-panel: #F1FFF7; - --background-butons__admin-panel: #0B6C61; - --button-color__admin-panel: #0B6C61; -} \ No newline at end of file + --background-input__admin-panel: #FFF; + --border-input__admin-panel: #d9d9d9; + --background-butons__admin-panel: #1F9A7C; + --button-color__admin-panel: #1F9A7C; +} diff --git a/FrontEnd/src/pages/AdminPage/AdminProfile/AdminInfo.jsx b/FrontEnd/src/pages/AdminPage/AdminProfile/AdminInfo.jsx new file mode 100644 index 000000000..d4e2455c3 --- /dev/null +++ b/FrontEnd/src/pages/AdminPage/AdminProfile/AdminInfo.jsx @@ -0,0 +1,139 @@ +import axios from 'axios'; +import { toast } from 'react-toastify'; +import { useForm } from 'react-hook-form'; + +import AdminSubmitButton from '../../../components/MiniComponents/AdminSubmitButton'; + +import classes from './AdminInfo.module.css'; + +const AdminInfo = ({ user, mutate }) => { + const { + register, + handleSubmit, + setValue, + getValues, + formState: { errors, isDirty }, + } = useForm({ + defaultValues: { + 'name': user.name, + 'surname': user.surname, + }, + }); + + const onSubmit = (data) => { + axios.patch(`${process.env.REACT_APP_BASE_API_URL}/api/auth/users/me/`, data) + .then(() => { + toast.success('Зміни успішно збережено'); + mutate(); + }) + .catch((error) => { + console.error( + 'Помилка:', + error.response ? error.response.data : error.message + ); + if (!error.response || error.response.status !== 401) { + toast.error('Не вдалося зберегти зміни, сталася помилка'); + } + }); + }; + + 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 000000000..7b5eeff6a --- /dev/null +++ b/FrontEnd/src/pages/AdminPage/AdminProfile/AdminInfo.module.css @@ -0,0 +1,109 @@ +.admin-info-form { + display: flex; + padding: 0px 24px; +} + +.admin-info-form__container { + display: flex; + flex-direction: column; + flex-wrap: wrap; +} + +.admin-info-form__column { + display: flex; + width: 257px; + min-height: 84px; + flex-direction: column; + align-items: flex-start; + gap: 4px; +} + +.admin-info-form__label { + display: flex; + gap: 4px; + align-self: stretch; + color: var(--font-color__admin-panel); + 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; +} + +.admin-info-form__label--hint { + align-self: stretch; + font-size: 8px; + line-height: 16px; + position: relative; + top: 0px; +} + +.admin-info-form__label--required { + color: var(--notification-text-color__admin-panel); + 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%; +} + +.admin-info-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); +} + +.admin-info-form__field { + display: flex; + align-items: center; + align-self: stretch; + border-radius: 2px; + background: var(--background-input__admin-panel); +} + +.admin-info-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(--border-input__admin-panel); + background: var(--background-input__admin-panel); + 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; +} + +.admin-info-form__input:focus { + border: 1px solid var(--main-header-color); + outline: none; +} + +.admin-info-form__error { + display: flex; + flex-direction: column; + align-items: flex-start; + align-self: stretch; + color: var(--notification-text-color__admin-panel); + font-family: var(--font-messages); + font-size: 12px; + font-style: normal; + font-weight: 400; + line-height: 16px; +} diff --git a/FrontEnd/src/pages/AdminPage/AdminProfile/AdminProfilePage.jsx b/FrontEnd/src/pages/AdminPage/AdminProfile/AdminProfilePage.jsx new file mode 100644 index 000000000..89f7fc9ad --- /dev/null +++ b/FrontEnd/src/pages/AdminPage/AdminProfile/AdminProfilePage.jsx @@ -0,0 +1,54 @@ +import { NavLink, Route, Routes } from 'react-router-dom'; + +import { useAuth } from '../../../hooks'; + +import BreadCrumbs from '../../../components/BreadCrumbs/BreadCrumbs'; +import ChangeAdminPassword from './ChangeAdminPassword'; +import AdminInfo from './AdminInfo'; + +import classes from './AdminProfilePage.module.css'; + +const ADMIN_PAGE_TABS = [ + { + title: 'Загальна інформація', + link: '/admin-info', + }, + { + title: 'Змінити пароль', + link: '/change-password', + }, +]; + +const AdminProfilePage = () => { + + const { user, mutate } = useAuth(); + + return ( +
+ +
+
+ {ADMIN_PAGE_TABS.map((element) => ( + (`${classes['infolink']} ${isActive && classes['infolink__active']}`)} + to={`/customadmin/admin-profile${element.link}`} + key={element.title} + > + {element.title} + + ))} +
+ + } /> + } /> + +
+
+ ); +}; + +export default AdminProfilePage; \ No newline at end of file diff --git a/FrontEnd/src/pages/AdminPage/AdminProfile/AdminProfilePage.module.css b/FrontEnd/src/pages/AdminPage/AdminProfile/AdminProfilePage.module.css new file mode 100644 index 000000000..f377308ca --- /dev/null +++ b/FrontEnd/src/pages/AdminPage/AdminProfile/AdminProfilePage.module.css @@ -0,0 +1,39 @@ +.admin-profile__container { + margin-top: 40px; + display: flex; + align-items: flex-start; + gap: 20px; + flex-direction: column; +} + +.admin-profile__content { + display: flex; + flex-direction: row; +} + +.admin-profile__links { + display: flex; + flex-direction: column; + padding-left: 10%; + align-items: flex-start; + width: var(--menu-size__admin-panel); + gap: 12px; +} + +.infolink { + color: var(--font-color__admin-panel); + font-family: var(--font-main); + font-size: 14px; + font-weight: 400; + line-height: 20px; + letter-spacing: -0.01em; + text-align: left; + text-decoration: none; +} + +.infolink__active { + font-family: var(--font-main); + font-weight: 600; + color: #000000; + text-decoration: none; +} diff --git a/FrontEnd/src/pages/AdminPage/AdminProfile/ChangeAdminPassword.jsx b/FrontEnd/src/pages/AdminPage/AdminProfile/ChangeAdminPassword.jsx new file mode 100644 index 000000000..a9b33873e --- /dev/null +++ b/FrontEnd/src/pages/AdminPage/AdminProfile/ChangeAdminPassword.jsx @@ -0,0 +1,115 @@ +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 AdminSubmitButton from '../../../components/MiniComponents/AdminSubmitButton'; + +import classes from './ChangeAdminPassword.module.css'; + +export default function ChangeAdminPassword(props) { + + const { + register, + handleSubmit, + getValues, + watch, + reset, + formState: { errors, isDirty }, + } = 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 000000000..e70e6bbcd --- /dev/null +++ b/FrontEnd/src/pages/AdminPage/AdminProfile/ChangeAdminPassword.module.css @@ -0,0 +1,14 @@ +.form__container { + display: flex; + flex-direction: column; + padding: 0px 24px; + word-wrap: break-word; + +} + +.form-new-password { + display: flex; + flex-direction: row; + gap: 8px; + align-items: center; +} diff --git a/FrontEnd/src/pages/AdminPage/Header/Header.jsx b/FrontEnd/src/pages/AdminPage/Header/Header.jsx deleted file mode 100644 index 6fe6c511a..000000000 --- a/FrontEnd/src/pages/AdminPage/Header/Header.jsx +++ /dev/null @@ -1,16 +0,0 @@ -import css from './Header.module.css'; -import { Link } from 'react-router-dom'; - -function Header() { - return ( -
-
- admin panel - Переглянути сайт -
-
-
- ); -} - -export default Header; diff --git a/FrontEnd/src/pages/AdminPage/Header/Header.module.css b/FrontEnd/src/pages/AdminPage/Header/Header.module.css deleted file mode 100644 index 10c4f48a9..000000000 --- a/FrontEnd/src/pages/AdminPage/Header/Header.module.css +++ /dev/null @@ -1,51 +0,0 @@ -.header-content { - display: flex; - margin: 10px; - justify-content: space-between; - align-items: center; -} - -.header-divider { - width: 100%; - height: 2px; - background: var(--main-black-20, #E2E5EB); -} - -.header-logo__text { - color: #292E32; - font-feature-settings: 'calt' off; - text-decoration: none; - font-family: var(--font__admin-panel); - font-size: 24px; - font-style: normal; - font-weight: 500; - line-height: 24px; - letter-spacing: -0.24px; -} - -.header-view__button { - display: flex; - padding: 5px 15px; - justify-content: center; - align-items: center; - gap: 10px; - - border-radius: 4px; - border: 1px solid var(--button-color__admin-panel); - background: var(--button-color__admin-panel); - - box-shadow: 0px 2px 0px 0px rgba(0, 0, 0, 0.04); - color: #FFF; - text-align: center; - font-family: var(--font__admin-panel); - font-size: 16px; - font-style: normal; - font-weight: 600; - line-height: 20px; - letter-spacing: -0.16px; -} - -.header-view__button:hover { - background: #FFF; - color: var(--button-color__admin-panel); -} \ No newline at end of file diff --git a/FrontEnd/src/pages/AdminPage/MainPage/MainPage.jsx b/FrontEnd/src/pages/AdminPage/MainPage/MainPage.jsx index e560d3e18..5bb5d5eb6 100644 --- a/FrontEnd/src/pages/AdminPage/MainPage/MainPage.jsx +++ b/FrontEnd/src/pages/AdminPage/MainPage/MainPage.jsx @@ -4,8 +4,8 @@ import css from './mainPage.module.css'; function MainPage() { return (
-

Welcome to the admin panel

-

Here, you can:

+

Вітаємо в панелі адміністратора.

+

Тут ви можете:


{MAIN_PAGE_TEXT.map((element) => (
diff --git a/FrontEnd/src/pages/AdminPage/MainPage/Text.js b/FrontEnd/src/pages/AdminPage/MainPage/Text.js index 808413785..a0390b101 100644 --- a/FrontEnd/src/pages/AdminPage/MainPage/Text.js +++ b/FrontEnd/src/pages/AdminPage/MainPage/Text.js @@ -1,22 +1,26 @@ export const MAIN_PAGE_TEXT = [ { - title: 'Check Profiles:', - text: 'Review user and company profiles.' + title: 'Перевіряти профілі:', + text: 'Переглянути профілі користувачів та компаній.' }, { - title: 'Edit Profiles:', - text: 'Make necessary updates to profiles for accuracy.' + title: 'Редагувати профілі:', + text: 'Внести необхідні зміни в профілі.' }, { - title: 'Approve Profiles:', - text: 'Give the green light to new profiles.' + title: 'Затверджувати профілі:', + text: 'Підтвердити дійсність нових профілів та затвердити зміни в існуючих.' }, { - title: 'Delete Profiles:', - text: 'In case of policy violations or other issues.' + title: 'Видаляти профілі:', + text: 'Видалити профіль у разі порушення правил користування сайтом або з інших причин.' }, { - title: 'Set autoapprove timeout:', - text: 'Specify the time (from 1 to 48 hours) after which the user\'s request to change the banner/logo will be approved automatically.' + title: 'Встановлювати таймаут для автозатвердження:', + text: 'Вказати час (від 1 до 48 годин), після якого запит користувача на зміну банера/логотипу буде підтверджено автоматично.' + }, + { + title: 'Встановити електронну адресу модератора:', + text: 'Вказати електронну адресу, на яку будуть приходити листи із запитом на затвердження зміни банера/логотипу в профілі.' } ]; diff --git a/FrontEnd/src/pages/AdminPage/MainPage/mainPage.module.css b/FrontEnd/src/pages/AdminPage/MainPage/mainPage.module.css index 3484ba4b4..556778fd5 100644 --- a/FrontEnd/src/pages/AdminPage/MainPage/mainPage.module.css +++ b/FrontEnd/src/pages/AdminPage/MainPage/mainPage.module.css @@ -6,10 +6,10 @@ } .main-page-section h1 { - font-size: 40px; + font-size: 20px; margin-bottom: 50px; } .main-page-section p { - font-size: 30px; + font-size: 20px; } diff --git a/FrontEnd/src/pages/AdminPage/Menu/Menu.jsx b/FrontEnd/src/pages/AdminPage/Menu/Menu.jsx index a1995dc15..93544cea8 100644 --- a/FrontEnd/src/pages/AdminPage/Menu/Menu.jsx +++ b/FrontEnd/src/pages/AdminPage/Menu/Menu.jsx @@ -1,8 +1,6 @@ -import css from './Menu.module.css'; -import { Link } from 'react-router-dom'; -import { useAuth } from '../../../hooks'; -import axios from 'axios'; +import { NavLink } from 'react-router-dom'; +import css from './Menu.module.css'; const MENU = [ { @@ -38,23 +36,15 @@ const MENU = [ ]; function Menu() { - const { isAuth, logout } = useAuth(); - const handleLogout = async () => { - if (isAuth) { - axios.post(`${process.env.REACT_APP_BASE_API_URL}/api/auth/token/logout`) - .then(() => { - logout(); - }); - } - }; return (
{MENU.map((element) => ( - {element.title} + (`${css['menu-section-element']} ${isActive && css['menu-section-element__active']}`)} + key={element.id} to={element.link}>{element.title} + ))} -
-
); } diff --git a/FrontEnd/src/pages/AdminPage/Menu/Menu.module.css b/FrontEnd/src/pages/AdminPage/Menu/Menu.module.css index 1309e1632..93170f61f 100644 --- a/FrontEnd/src/pages/AdminPage/Menu/Menu.module.css +++ b/FrontEnd/src/pages/AdminPage/Menu/Menu.module.css @@ -1,7 +1,7 @@ .menu-section { display: flex; width: var(--menu-size__admin-panel); - padding: 20px; + padding: 30px; flex-direction: column; align-items: flex-start; gap: 23px; @@ -14,26 +14,11 @@ font-family: var(--font__admin-panel); font-size: 20px; font-style: normal; - font-weight: 600; + font-weight: 400; line-height: 22px; letter-spacing: -0.14px; } -.menu-section-divider { - height: 1px; - align-self: stretch; - width: var(--menu-size__admin-panel); - background: var(--main-black-20, #E2E5EB); -} - -.menu-section-logout { - color: var(--system-red-100, #F15831); - font-feature-settings: 'calt' off; - text-decoration: none; - font-family: var(--font__admin-panel); - font-size: 20px; - font-style: normal; +.menu-section-element__active { font-weight: 600; - line-height: 22px; - letter-spacing: -0.14px; -} \ No newline at end of file +} diff --git a/FrontEnd/src/pages/ProfilePage/FormComponents/ChangePassword.jsx b/FrontEnd/src/pages/ProfilePage/FormComponents/ChangePassword.jsx index 32b408fd7..d466d07bf 100644 --- a/FrontEnd/src/pages/ProfilePage/FormComponents/ChangePassword.jsx +++ b/FrontEnd/src/pages/ProfilePage/FormComponents/ChangePassword.jsx @@ -71,7 +71,6 @@ export default function ChangePassword(props) { label="Поточний пароль" register={register} error={errors} - showError={false} watch={watch} checkValid={false} checkMatch={{ @@ -85,7 +84,6 @@ export default function ChangePassword(props) { label="Новий пароль" error={errors} register={register} - showError={true} watch={watch} checkValid={true} checkMatch={{ diff --git a/FrontEnd/src/pages/ProfilePage/FormComponents/FormFields/PasswordField.jsx b/FrontEnd/src/pages/ProfilePage/FormComponents/FormFields/PasswordField.jsx index 79359eadc..5f0060e1f 100644 --- a/FrontEnd/src/pages/ProfilePage/FormComponents/FormFields/PasswordField.jsx +++ b/FrontEnd/src/pages/ProfilePage/FormComponents/FormFields/PasswordField.jsx @@ -1,11 +1,15 @@ import { useState } from 'react'; import { PropTypes } from 'prop-types'; +import classNames from 'classnames'; + import EyeInvisible from '../../../Authorization/EyeInvisible'; import EyeVisible from '../../../Authorization/EyeVisible'; import preventEnterSubmit from '../../../../utils/preventEnterSubmit'; -import css from './PasswordField.module.css'; + import { PASSWORD_PATTERN } from '../../../../constants/constants'; +import css from './PasswordField.module.css'; + const PasswordField = (props) => { const errorMessages = { invalidPassword: 'Пароль не відповідає вимогам', @@ -34,11 +38,18 @@ const PasswordField = (props) => {
+ {label !== 'Поточний пароль' && + + }
@@ -47,7 +58,6 @@ const PasswordField = (props) => { id={inputId} type={showPassword ? 'text' : 'password'} placeholder={label} - required {...register(name, { required: errorMessages.requiredField, pattern: checkValid && { diff --git a/FrontEnd/src/pages/ProfilePage/FormComponents/FormFields/PasswordField.module.css b/FrontEnd/src/pages/ProfilePage/FormComponents/FormFields/PasswordField.module.css index bdc08c98b..1d9636d5b 100644 --- a/FrontEnd/src/pages/ProfilePage/FormComponents/FormFields/PasswordField.module.css +++ b/FrontEnd/src/pages/ProfilePage/FormComponents/FormFields/PasswordField.module.css @@ -6,10 +6,10 @@ align-items: flex-start; } -.password-field__item label { +.password-field__label--content { font-feature-settings: "calt" off; align-items: center; - color: #292e32; + color: var(--main-black-90); display: flex; font-family: var(--font-main); font-size: 14px; @@ -18,11 +18,24 @@ gap: 4px; letter-spacing: -.14px; line-height: 20px; - padding-bottom: 8px; +} + +.password-field__label--gap { + padding-bottom: 10px; } .password-field__label-wrapper { - padding-bottom: 9px; + display: flex; + flex-direction: column; + margin-left: 10px; +} + +.password-field__label--hint { + align-self: stretch; + font-size: 8px; + line-height: 16px; + position: relative; + top: 0px; } .password-field__label-wrapper span { @@ -33,6 +46,7 @@ .password-field__item input { border: none; } + .password-field__item input:focus { border: none; outline: none; @@ -48,6 +62,7 @@ text-align: left; color: #00000040; } + .password-field__password { display: flex; padding: 4px 12px; @@ -70,7 +85,7 @@ border-radius: 2px; border: 1px solid #1f9a7c; background: #fff; - } +} .password-field__password__wrapper { display: flex; @@ -98,7 +113,7 @@ .error-dot::before { position: absolute; - margin-left: -10px; + margin-left: -8px; content: "*"; color: #ff4d4f; text-align: right; @@ -107,4 +122,4 @@ font-style: normal; font-weight: 400; line-height: 22.001px; - } \ No newline at end of file +} diff --git a/FrontEnd/src/pages/SearchPage/tests/Search.test.js b/FrontEnd/src/pages/SearchPage/tests/Search.test.js index 3c56e8436..f5a1dd22d 100644 --- a/FrontEnd/src/pages/SearchPage/tests/Search.test.js +++ b/FrontEnd/src/pages/SearchPage/tests/Search.test.js @@ -3,6 +3,10 @@ import { MemoryRouter } from 'react-router-dom'; import Search from '../Search'; +jest.mock('../../../hooks', () => ({ + useAuth: () => ({isStaff: false}), +})); + afterEach(() => { jest.resetAllMocks(); }); diff --git a/FrontEnd/src/routes/AdminRouter.jsx b/FrontEnd/src/routes/AdminRouter.jsx index 25e8b3c2e..a9fc3c41e 100644 --- a/FrontEnd/src/routes/AdminRouter.jsx +++ b/FrontEnd/src/routes/AdminRouter.jsx @@ -1,25 +1,30 @@ import { ToastContainer } from 'react-toastify'; -import '../pages/AdminPage/AdminGlobal.css'; -import Header from '../pages/AdminPage/Header/Header'; +import { Routes, Route, useLocation } from 'react-router-dom'; + +import { useAuth } from '../hooks'; + +import Header from '../components/Header/Header'; import Menu from '../pages/AdminPage/Menu/Menu'; import UserDetail from '../pages/AdminPage/DetailView/UserDetail'; import UserTable from '../pages/AdminPage/UserProfilesTable/UserTable'; import ProfilesTable from '../pages/AdminPage/UserProfilesTable/ProfilesTable'; import ProfileDetail from '../pages/AdminPage/DetailView/ProfileDetail'; -import css from '../pages/AdminPage/AdminPage.module.css'; -import { Routes, Route } from 'react-router-dom'; import MainPage from '../pages/AdminPage/MainPage/MainPage'; -import { useAuth } from '../hooks'; 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'; import AdminRegistration from '../pages/AdminPage/AdminRegistration/AdminRegistration'; +import '../pages/AdminPage/AdminGlobal.css'; +import css from '../pages/AdminPage/AdminPage.module.css'; function AdminRouter() { - const { isLoading, isAuth, isStaff } = useAuth(); - const renderMenu = isStaff && isAuth ? : null; + const { isLoading, isAuth, isStaff, user } = useAuth(); + const { pathname } = useLocation(); + const hideMenu = pathname.includes('/admin-profile/'); + const renderMenu = isStaff && isAuth && !hideMenu ? : null; const authRoutes = isStaff && isAuth ? ( <> } /> @@ -30,6 +35,7 @@ function AdminRouter() { } /> } /> } /> + } /> } /> ) : ( @@ -38,7 +44,7 @@ function AdminRouter() { return (
-
+
{isLoading ? :
{renderMenu}