@@ -60,7 +60,8 @@ margin: 0; } -html {} +html { +} body { max-width: 100vw; @@ -207,7 +208,8 @@ p { } /* Примените фиксированную ширину, если необходимо */ -.MuiMenu-paper {} +.MuiMenu-paper { +} .pd-30 { padding: 30px; @@ -368,7 +370,7 @@ textarea { width: 150px !important; } -.rdw-dropdown-optionwrapper>li { +.rdw-dropdown-optionwrapper > li { color: inherit !important; } @@ -2,17 +2,18 @@ import axios from 'axios' import { API_URL } from '#/shared/lib/constants' -export const changePassword = async (email: string, password: string, token?: string) => { - return await axios.patch( +export const changePassword = async (email: string, password: string, newPassword: string, token?: string) => { + return await axios.patch( API_URL + `/auth/business-host/account/change-pass/${email}`, { password_1: password, - password_2: password, + password_2: newPassword, }, { headers: { Authorization: `Bearer ${token}`, }, + validateStatus: (status) => status < 500 } ) } @@ -0,0 +1 @@ +const ERRORS_TRANSLATE = {'Passwords don\'t match': ''} \ No newline at end of file @@ -7,6 +7,7 @@ import { useForm } from 'react-hook-form' interface ChangePasswordForm { password: string + newPassword: string } export const useChangePassword = () => { @@ -18,25 +19,38 @@ export const useChangePassword = () => { trigger, watch, handleSubmit, + setError, setValue, } = useForm() const { showMessage } = useShowDataStore() const { data: session } = useSession() + const [passwordShowed, setPasswordShowed] = useState(false) const onSubmit = handleSubmit(async (data, event) => { if (!session) return - const respose = await changePassword(modal.getStoreProperty('email')!, data.password, session.access) + if (data.password !== data.newPassword) { + showMessage('Пароли не совпадают!', 'error') + setError('password', { message: 'Пароли не совпадают' }) + return setError('newPassword', { message: 'Пароли не совпадают' }) + } + + const { data: res, status } = await changePassword( + modal.getStoreProperty('email')!, + data.password, + data.newPassword, + session.access + ) - if (respose.status == 200) { + if (status == 200) { showMessage('Пароль изменен успешно!', 'success') modal.setState(false) - } else { - showMessage(respose.data, 'error') + return } + showMessage((res as { detail: string }).detail) reset() }) - return { onSubmit, register, watch, errors, trigger, setValue } + return { onSubmit, register, watch, reset, errors, trigger, setValue, passwordShowed, setPasswordShowed } } @@ -1,20 +1,30 @@ .container { - padding-bottom: 0; + padding-bottom: 0 !important; } .modal { min-width: 450px; width: 100%; + @media screen and (max-width: 600px) { + min-width: 350px; + } + &__buttons { display: flex; margin-top: 45px; gap: 10px; } &__header { - margin-bottom: 35px; + margin: 35px 0px 35px 0px; font-size: 30px; font-weight: 600; letter-spacing: -0.02em; } } + +.box { + display: flex; + flex-direction: column; + gap: 25px; +} @@ -4,31 +4,79 @@ import React, { useEffect } from 'react' import { CommonButton } from '#/shared/ui/button' import { CommonInput } from '#/shared/ui/common-input' import { useChangePassword } from '../lib/use-change-password' +import OpenedEyeSvg from '#/assets/svg/opened-eye.svg?react' +import ClosedEyeSvg from '#/assets/svg/closed-eye.svg?react' export const ChangePasswordPlate = () => { const modal = getModalById(PLATE_CHANGE_PASSWORD) - const { onSubmit, register, errors, trigger, watch, setValue } = useChangePassword() + const { onSubmit, register, errors, reset, trigger, watch, setValue, passwordShowed, setPasswordShowed } = useChangePassword() return ( - + { + reset() + }} + >

Смена пароля

- { - setValue('password', e.target.value) - trigger('password') - }} - label='Новый пароль' - variant='outline' - placeholder='Пароль' - error={errors.password} - // onInput={() => trigger('password')} - /> +
+ { + setValue('password', e.target.value) + trigger('password') + }} + label='Новый пароль' + variant='outline' + placeholder='Пароль' + autoComplete='new-password' + error={errors.password} + type={passwordShowed ? '' : 'password'} + rightSlot={ + + } + // onInput={() => trigger('password')} + /> + { + setValue('newPassword', e.target.value) + trigger('newPassword') + }} + label='Повторить пароль' + variant='outline' + placeholder='Повторить пароль' + type={passwordShowed ? '' : 'password'} + rightSlot={ + + } + error={errors.newPassword} + // onInput={() => trigger('password')} + /> +
Сменить пароль @@ -37,6 +85,7 @@ export const ChangePasswordPlate = () => { variant='gray' onClick={() => { modal.setState(false) + reset() }} type='button' > @@ -71,22 +71,28 @@ overflow: hidden; height: fit-content; transition: all 0.5s ease-in-out; - } - &__header { - display: flex; - justify-content: space-between; - padding: 25px; + &_secondary { + background-color: #373737; + } } + // &__header { + // display: flex; + // justify-content: space-between; + // padding: 25px; + // } + &__close-button { - position: relative; + top: 25px; + right: 25px; + position: absolute; z-index: 1; transform: rotate(45deg); } &__content-body { - transition: all 0.5s ease-in-out; + transition: all 0.3s ease-in-out; padding: 0px 32px 32px 32px; overflow: hidden; @@ -100,6 +106,21 @@ } &__animation-behavior { - transform: translateX(50px); + opacity: 0; + } +} + +html[data-theme='light'] { + .template__content { + background-color: var(--new-ui-main-color); + border-radius: 20px; + position: relative; + overflow: hidden; + height: fit-content; + transition: all 0.5s ease-in-out; + + &_secondary { + background-color: white; + } } } @@ -9,6 +9,7 @@ import { useThemeAndDevice } from '#/shared/lib/hooks' export type AlignX = 'left' | 'center' | 'right' export type AlignY = 'top' | 'center' | 'bottom' +export type Variant = 'primary' | 'secondary' export interface PlatesTemplateProps { id: string @@ -22,6 +23,7 @@ export interface PlatesTemplateProps { animationClass?: string animationBehaviorClass?: string headerClassName?: string + variant?: Variant } export default function PlatesTemplate({ @@ -34,7 +36,9 @@ export default function PlatesTemplate({ hasBg = true, animationClass = styles['template__animation'], animationBehaviorClass = styles['template__animation-behavior'], + closeModal = () => {}, headerClassName, + variant = 'primary', }: PlatesTemplateProps) { const { setModal, getModal } = usePlatesStore() @@ -59,15 +63,23 @@ export default function PlatesTemplate({ > {hasTemplate ? (
e.stopPropagation()} > -
-

{title}

- -
+
{children}
) : ( @@ -10,7 +10,7 @@ interface ShowDataStore { setOpened: (isOpened: boolean) => void setMessage: (message: string) => void setVariant: (variant: Variant) => void - showMessage: (message: string, variant: Variant) => void + showMessage: (message: string, variant?: Variant) => void } export const useShowDataStore = create((set, get) => { @@ -58,7 +58,7 @@ } &_gray { - background-color: var(--bg-color-button-gray); + background-color: #f2f2f2; color: var(--air-color); border-radius: 12px; font-weight: 500; @@ -70,8 +70,9 @@ } } -body[data-theme='dark'] { +html[data-theme='dark'] { .button_gray { - color: white; + color: white !important; + background-color: #5d5a5a !important; } } @@ -7,14 +7,14 @@ display: flex; align-items: center; justify-content: space-between; - padding-top: 10px; &__error { transform: translateY(16px); - font-weight: bold; - color: red; + color: #ff2372; + font-size: 13px; opacity: 0; transition: all 300ms; + margin-top: 8px; &_visible { opacity: 1; @@ -27,35 +27,93 @@ color: #97989f; font-size: 14px; margin-bottom: 8px; + + &_error { + color: #97989f; + font-size: 14px; + margin-bottom: 8px; + } } .input { background-color: transparent; + border: none; + outline: none; font-size: 16px; + width: 100%; + &:focus { border: none; outline: none; } &_outline { - color: var(--text-color-main); - padding: 20px; + padding: 20px 0; + color: var(--text-color-main) !important; + + &::placeholder { + color: #97989f !important; + } + } + + &_error { + color: #ff2372 !important; + &::placeholder { + color: #ff2372 !important; + } + } +} + + +.wrapper { + display: flex; + justify-content: space-between; + align-items: center; + + &_outline { + color: var(--text-color-main) !important; + padding: 0 20px; border: 2px solid var(--background-color-table); border-radius: 13px; + &::placeholder { - color: #97989f; + color: #97989f !important; } + &:focus { border: 2px solid var(--background-color-table); } } + + &_error { + border-color: #ff2372; + + &::placeholder { + color: #ff2372 !important; + } + + &:focus { + border: 2px solid #ff2372; + } + } } html[data-theme='light'] { - .input_outline { - border: 2px solid #f2f2f8; + // Переопределяем стили для светлой темы + .wrapper_outline { + border: 2px solid #f2f2f8 !important; + &:focus { + border: 2px solid var(--background-color-table) !important; + } + } + + .wrapper_error { + border-color: #ff76a7 !important; + &::placeholder { + color: #ff76a7 !important; + } &:focus { - border: 2px solid #f2f2f8; + border: 2px solid #ff76a7 !important; } } } @@ -10,20 +10,35 @@ export interface CommonInputProps extends React.InputHTMLAttributes { +export const CommonInput = ({ children, rightSlot, variant = 'outline', label, error, className, disabled, ...props }: CommonInputProps) => { return (
-
- {label && } -

{error ? error?.message : ''}

+
{label && }
+
+ + {rightSlot}
- +

{error ? error?.message : ''}

) } @@ -18,14 +18,14 @@ const ErrorIcon = () => { } export const Error: React.FC = ({ handleClose }) => { - const { message, isError, isOpened } = useShowDataStore() + const { message, variant, isOpened } = useShowDataStore() const { theme } = useThemeAndDevice() const nodeRef = useRef(null) return ( - {isError ? ( + {variant == 'error' ? ( } @@ -56,7 +56,7 @@ export const PersonsList = ({ return [data] }) - showMessage('Пользователь успешно приглашен!') + showMessage('Пользователь успешно приглашен!', 'success') } const [search, setSearch] = useState('') @@ -77,7 +77,7 @@ export const PersonsList = ({ }) ) setCurrentPerson(null) - showMessage(`Лимит пользователя ${email} успешно изменён!`) + showMessage(`Лимит пользователя ${email} успешно изменён!`, 'success') } useEffect(() => { @@ -61,7 +61,7 @@ export const Layout: React.FC = ({ children, titlePage, title = tit useEffect(() => { if (!data) return getUserData(data.access) - }, [data]) + }, [data?.access]) useEffect(() => { if ((sessionData && !sessionStorage.getItem('firstRender')) || (sessionData && !localStorage.getItem('global_settings'))) {