mirror of
https://github.com/muerwre/vault-frontend.git
synced 2025-04-25 04:46:40 +07:00
refactor auth components
This commit is contained in:
parent
1caf402ef3
commit
b3acef0a1e
21 changed files with 37 additions and 19 deletions
|
@ -0,0 +1,147 @@
|
|||
import { FC, memo, useCallback, useEffect, useRef, useState } from 'react';
|
||||
|
||||
import { throttle } from 'throttle-debounce';
|
||||
|
||||
import { useWindowSize } from '~/hooks/dom/useWindowSize';
|
||||
|
||||
import styles from './styles.module.scss';
|
||||
|
||||
interface LoginSceneProps {}
|
||||
|
||||
interface Layer {
|
||||
src: string;
|
||||
velocity: number;
|
||||
width: number;
|
||||
height: number;
|
||||
}
|
||||
|
||||
const layers: Layer[] = [
|
||||
{
|
||||
src: '/images/clouds__bg.svg',
|
||||
velocity: -0.3,
|
||||
width: 3840,
|
||||
height: 1080,
|
||||
},
|
||||
{
|
||||
src: '/images/clouds__cube.svg',
|
||||
velocity: -0.1,
|
||||
width: 3840,
|
||||
height: 1080,
|
||||
},
|
||||
{
|
||||
src: '/images/clouds__cloud.svg',
|
||||
velocity: 0.2,
|
||||
width: 3840,
|
||||
height: 1080,
|
||||
},
|
||||
{
|
||||
src: '/images/clouds__dudes.svg',
|
||||
velocity: 0.5,
|
||||
width: 3840,
|
||||
height: 1080,
|
||||
},
|
||||
{
|
||||
src: '/images/clouds__trash.svg',
|
||||
velocity: 0.8,
|
||||
width: 3840,
|
||||
height: 1080,
|
||||
},
|
||||
];
|
||||
|
||||
const LoginAnimatedScene: FC<LoginSceneProps> = memo(() => {
|
||||
const containerRef = useRef<HTMLDivElement>(null);
|
||||
const [loaded, setLoaded] = useState(false);
|
||||
const imageRefs = useRef<Array<SVGImageElement | null>>([]);
|
||||
const { isTablet } = useWindowSize();
|
||||
const domRect = useRef<DOMRect>();
|
||||
|
||||
const onMouseMove = useCallback(
|
||||
(event: MouseEvent): any => {
|
||||
if (!containerRef.current) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!domRect.current) {
|
||||
domRect.current = containerRef.current.getBoundingClientRect();
|
||||
}
|
||||
|
||||
const { x, width } = domRect.current!;
|
||||
const middle = (width - x) / 2;
|
||||
const shift = event.pageX / middle / 2 - 0.5;
|
||||
|
||||
layers.forEach((it, index) => {
|
||||
const target = imageRefs.current[index];
|
||||
|
||||
if (target) {
|
||||
target.style.transform = `translate(${
|
||||
shift * it.velocity * 200
|
||||
}px, 0)`;
|
||||
}
|
||||
});
|
||||
},
|
||||
[containerRef],
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
const listener = throttle(100, onMouseMove);
|
||||
document.addEventListener('mousemove', listener);
|
||||
return () => document.removeEventListener('mousemove', listener);
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, []);
|
||||
|
||||
if (isTablet) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<div className={styles.scene} ref={containerRef}>
|
||||
<svg
|
||||
width="100%"
|
||||
height="100%"
|
||||
viewBox="0 0 1920 1080"
|
||||
preserveAspectRatio="xMidYMid slice"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<defs>
|
||||
<linearGradient id="fallbackGradient" x1={0} x2={0} y1={1} y2={0}>
|
||||
<stop style={{ stopColor: '#ffccaa', stopOpacity: 1 }} offset="0" />
|
||||
<stop
|
||||
style={{ stopColor: '#fff6d5', stopOpacity: 1 }}
|
||||
offset="0.34655526"
|
||||
/>
|
||||
<stop
|
||||
style={{ stopColor: '#afc6e9', stopOpacity: 1 }}
|
||||
offset="0.765342"
|
||||
/>
|
||||
<stop style={{ stopColor: '#879fde', stopOpacity: 1 }} offset="1" />
|
||||
</linearGradient>
|
||||
</defs>
|
||||
|
||||
<rect
|
||||
width={1920}
|
||||
height={1080}
|
||||
x={0}
|
||||
y={0}
|
||||
fill="url(#fallbackGradient)"
|
||||
/>
|
||||
|
||||
{layers.map((it) => (
|
||||
<image
|
||||
ref={(it) => imageRefs.current.push(it)}
|
||||
key={it.src}
|
||||
href={it.src}
|
||||
width={it.width}
|
||||
height={it.height}
|
||||
x={1920 / 2 - it.width / 2}
|
||||
y={0}
|
||||
opacity={loaded ? 1 : 0}
|
||||
onLoad={() => setLoaded(true)}
|
||||
className={styles.image}
|
||||
/>
|
||||
))}
|
||||
</svg>
|
||||
</div>
|
||||
);
|
||||
});
|
||||
|
||||
export { LoginAnimatedScene };
|
|
@ -0,0 +1,18 @@
|
|||
@import "src/styles/mixins";
|
||||
|
||||
.scene {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.image {
|
||||
transition: opacity 1s, transform 0.1s;
|
||||
will-change: opacity, transform;
|
||||
|
||||
@include tablet {
|
||||
display: none;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,41 @@
|
|||
import React, { FC } from 'react';
|
||||
|
||||
import { Grid } from '~/components/containers/Grid';
|
||||
import { Group } from '~/components/containers/Group';
|
||||
import { Button } from '~/components/input/Button';
|
||||
import { OAuthProvider } from '~/types/auth';
|
||||
|
||||
import styles from './styles.module.scss';
|
||||
|
||||
|
||||
interface IProps {
|
||||
openOauthWindow: (provider: OAuthProvider) => void;
|
||||
}
|
||||
|
||||
const LoginDialogButtons: FC<IProps> = ({ openOauthWindow }) => (
|
||||
<Group className={styles.footer}>
|
||||
<Button>Войти</Button>
|
||||
|
||||
<Grid columns="repeat(2, 1fr)">
|
||||
<Button
|
||||
color="outline"
|
||||
iconLeft="google"
|
||||
type="button"
|
||||
onClick={() => openOauthWindow('google')}
|
||||
>
|
||||
<span>Google</span>
|
||||
</Button>
|
||||
|
||||
<Button
|
||||
color="outline"
|
||||
iconLeft="vk"
|
||||
type="button"
|
||||
onClick={() => openOauthWindow('vkontakte')}
|
||||
>
|
||||
<span>Вконтакте</span>
|
||||
</Button>
|
||||
</Grid>
|
||||
</Group>
|
||||
);
|
||||
|
||||
export { LoginDialogButtons };
|
|
@ -0,0 +1,5 @@
|
|||
@import 'src/styles/variables';
|
||||
|
||||
.footer {
|
||||
padding: $gap;
|
||||
}
|
|
@ -0,0 +1,15 @@
|
|||
import React, { FC } from 'react';
|
||||
|
||||
import { Button } from '~/components/input/Button';
|
||||
|
||||
import styles from './styles.module.scss';
|
||||
|
||||
interface IProps {}
|
||||
|
||||
const LoginSocialRegisterButtons: FC<IProps> = () => (
|
||||
<div className={styles.wrap}>
|
||||
<Button>Впустите меня!</Button>
|
||||
</div>
|
||||
);
|
||||
|
||||
export { LoginSocialRegisterButtons };
|
|
@ -0,0 +1,9 @@
|
|||
@import "src/styles/variables";
|
||||
|
||||
.wrap {
|
||||
padding: $gap $gap * 2;
|
||||
|
||||
button {
|
||||
width: 100%;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,15 @@
|
|||
import React, { FC } from 'react';
|
||||
|
||||
import classNames from 'classnames';
|
||||
|
||||
import styles from './styles.module.scss';
|
||||
|
||||
interface LoginStaticSceneProps {
|
||||
scene?: 'clouds' | 'nowhere';
|
||||
}
|
||||
|
||||
const LoginStaticScene: FC<LoginStaticSceneProps> = ({ scene = 'login' }) => (
|
||||
<div className={classNames(styles.scene, styles[scene])} />
|
||||
);
|
||||
|
||||
export { LoginStaticScene };
|
|
@ -0,0 +1,16 @@
|
|||
@import 'src/styles/mixins';
|
||||
|
||||
.scene {
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
|
||||
&.clouds {
|
||||
background: url('/images/clouds.svg') no-repeat 50% 50%;
|
||||
background-size: cover;
|
||||
}
|
||||
|
||||
&.nowhere {
|
||||
background: url('/images/nowhere_motel.svg') no-repeat 50% 50%;
|
||||
background-size: cover;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,46 @@
|
|||
import React, { FC } from 'react';
|
||||
|
||||
import TelegramLoginButton, {
|
||||
TelegramUser,
|
||||
} from '@v9v/ts-react-telegram-login';
|
||||
|
||||
import { LoaderCircle } from '~/components/input/LoaderCircle';
|
||||
|
||||
import styles from './styles.module.scss';
|
||||
|
||||
interface TelegramLoginFormProps {
|
||||
botName: string;
|
||||
loading?: boolean;
|
||||
onSuccess?: (token: TelegramUser) => void;
|
||||
}
|
||||
|
||||
const TelegramLoginForm: FC<TelegramLoginFormProps> = ({
|
||||
botName,
|
||||
loading,
|
||||
onSuccess,
|
||||
}) => {
|
||||
return (
|
||||
<div className={styles.container}>
|
||||
<div className={styles.text}>
|
||||
{loading ? (
|
||||
<LoaderCircle />
|
||||
) : (
|
||||
<div>
|
||||
После успешной авторизации аккаунт появится в настройках вашего
|
||||
профиля
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<div className={styles.button}>
|
||||
<TelegramLoginButton
|
||||
dataOnAuth={onSuccess}
|
||||
botName={botName}
|
||||
requestAccess
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export { TelegramLoginForm };
|
|
@ -0,0 +1,22 @@
|
|||
@import 'src/styles/variables';
|
||||
|
||||
.button {
|
||||
flex: 0 0 48px;
|
||||
}
|
||||
|
||||
.container {
|
||||
min-height: 200px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
flex-direction: column;
|
||||
padding: $gap;
|
||||
}
|
||||
|
||||
.text {
|
||||
text-align: center;
|
||||
flex: 1;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
|
@ -1,7 +1,5 @@
|
|||
import { FC, useCallback, useRef } from 'react';
|
||||
|
||||
import { LoginDialogButtons } from '~/components/auth/login/LoginDialogButtons';
|
||||
import { LoginStaticScene } from '~/components/auth/login/LoginStaticScene';
|
||||
import { Group } from '~/components/containers/Group';
|
||||
import { Padder } from '~/components/containers/Padder';
|
||||
import { BetterScrollDialog } from '~/components/dialogs/BetterScrollDialog';
|
||||
|
@ -9,6 +7,8 @@ import { DialogTitle } from '~/components/dialogs/DialogTitle';
|
|||
import { Button } from '~/components/input/Button';
|
||||
import { InputText } from '~/components/input/InputText';
|
||||
import { Dialog } from '~/constants/modal';
|
||||
import { LoginDialogButtons } from '~/containers/dialogs/LoginDialog/components/LoginDialogButtons';
|
||||
import { LoginStaticScene } from '~/containers/dialogs/LoginDialog/components/LoginStaticScene';
|
||||
import { useCloseOnEscape } from '~/hooks';
|
||||
import { useAuth } from '~/hooks/auth/useAuth';
|
||||
import { useLoginForm } from '~/hooks/auth/useLoginForm';
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
import React, { FC, useCallback, useState } from 'react';
|
||||
|
||||
import { apiLoginWithSocial } from '~/api/auth';
|
||||
import { LoginSocialRegisterButtons } from '~/components/auth/oauth/LoginSocialRegisterButtons';
|
||||
import { Group } from '~/components/containers/Group';
|
||||
import { Padder } from '~/components/containers/Padder';
|
||||
import { BetterScrollDialog } from '~/components/dialogs/BetterScrollDialog';
|
||||
|
@ -9,6 +8,7 @@ import { DialogTitle } from '~/components/dialogs/DialogTitle';
|
|||
import { InputText } from '~/components/input/InputText';
|
||||
import { Toggle } from '~/components/input/Toggle';
|
||||
import { getRandomPhrase } from '~/constants/phrases';
|
||||
import { LoginSocialRegisterButtons } from '~/containers/dialogs/LoginDialog/components/LoginSocialRegisterButtons';
|
||||
import { useCloseOnEscape } from '~/hooks';
|
||||
import { useSocialRegisterForm } from '~/hooks/auth/useSocialRegisterForm';
|
||||
import { useModal } from '~/hooks/modal/useModal';
|
||||
|
@ -35,13 +35,13 @@ const LoginSocialRegisterDialog: FC<LoginSocialRegisterDialogProps> = ({
|
|||
auth.setToken(loginToken);
|
||||
hideModal();
|
||||
},
|
||||
[auth, hideModal]
|
||||
[auth, hideModal],
|
||||
);
|
||||
|
||||
const { values, errors, handleChange, handleSubmit } = useSocialRegisterForm(
|
||||
token,
|
||||
apiLoginWithSocial,
|
||||
onSuccess
|
||||
onSuccess,
|
||||
);
|
||||
|
||||
return (
|
||||
|
@ -73,12 +73,20 @@ const LoginSocialRegisterDialog: FC<LoginSocialRegisterDialogProps> = ({
|
|||
autoComplete="new-password"
|
||||
/>
|
||||
|
||||
<div className={styles.check} onClick={() => setIsDryingPants(!isDryingPants)}>
|
||||
<div
|
||||
className={styles.check}
|
||||
onClick={() => setIsDryingPants(!isDryingPants)}
|
||||
>
|
||||
<Toggle value={isDryingPants} color="primary" />
|
||||
<span>Это не мои штаны сушатся на радиаторе в третьей лаборатории</span>
|
||||
<span>
|
||||
Это не мои штаны сушатся на радиаторе в третьей лаборатории
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<div className={styles.check} onClick={() => setIsDryingPants(!isDryingPants)}>
|
||||
<div
|
||||
className={styles.check}
|
||||
onClick={() => setIsDryingPants(!isDryingPants)}
|
||||
>
|
||||
<Toggle value={!isDryingPants} color="primary" />
|
||||
<span>{phrase}</span>
|
||||
</div>
|
||||
|
|
|
@ -0,0 +1,29 @@
|
|||
import React, { VFC } from 'react';
|
||||
|
||||
import { Group } from '~/components/containers/Group';
|
||||
import { Button } from '~/components/input/Button';
|
||||
import { Icon } from '~/components/input/Icon';
|
||||
import { ERROR_LITERAL, ERRORS } from '~/constants/errors';
|
||||
|
||||
import styles from './styles.module.scss';
|
||||
|
||||
interface RestoreInvalidCodeProps {
|
||||
error: string;
|
||||
onClose: () => void;
|
||||
}
|
||||
|
||||
const RestoreInvalidCode: VFC<RestoreInvalidCodeProps> = ({ error, onClose }) => (
|
||||
<Group className={styles.error_shade}>
|
||||
<Icon icon="close" size={64} />
|
||||
|
||||
<div>{error || ERROR_LITERAL[ERRORS.CODE_IS_INVALID]}</div>
|
||||
|
||||
<div className={styles.spacer} />
|
||||
|
||||
<Button color="primary" onClick={onClose}>
|
||||
Очень жаль
|
||||
</Button>
|
||||
</Group>
|
||||
);
|
||||
|
||||
export { RestoreInvalidCode };
|
|
@ -0,0 +1,35 @@
|
|||
@import 'src/styles/variables';
|
||||
|
||||
.error_shade {
|
||||
@include outer_shadow();
|
||||
|
||||
background: $content_bg;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
z-index: 1;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
border-radius: $radius;
|
||||
padding: $gap * 2;
|
||||
box-sizing: border-box;
|
||||
text-transform: uppercase;
|
||||
font: $font_18_semibold;
|
||||
text-align: center;
|
||||
color: $color_primary;
|
||||
|
||||
svg {
|
||||
fill: $color_primary;
|
||||
}
|
||||
}
|
||||
|
||||
.error_shade {
|
||||
color: $color_danger;
|
||||
|
||||
svg {
|
||||
fill: $color_danger;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,27 @@
|
|||
import React, { VFC } from 'react';
|
||||
|
||||
import { Group } from '~/components/containers/Group';
|
||||
import { Button } from '~/components/input/Button';
|
||||
import { Icon } from '~/components/input/Icon';
|
||||
|
||||
import styles from './styles.module.scss';
|
||||
|
||||
interface RestoreSuccessProps {
|
||||
username?: string;
|
||||
onClick: () => void;
|
||||
}
|
||||
|
||||
const RestoreSuccess: VFC<RestoreSuccessProps> = ({ username, onClick }) => (
|
||||
<Group className={styles.shade}>
|
||||
<Icon icon="check" size={64} />
|
||||
|
||||
<div>Пароль обновлен</div>
|
||||
<div>Добро пожаловать домой{username ? `, ~${username}` : ''}!</div>
|
||||
|
||||
<div />
|
||||
|
||||
<Button onClick={onClick}>Ура!</Button>
|
||||
</Group>
|
||||
);
|
||||
|
||||
export { RestoreSuccess };
|
|
@ -0,0 +1,27 @@
|
|||
@import 'src/styles/variables';
|
||||
|
||||
.shade {
|
||||
@include outer_shadow();
|
||||
|
||||
background: $content_bg;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
z-index: 1;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
border-radius: $radius;
|
||||
padding: $gap * 2;
|
||||
box-sizing: border-box;
|
||||
text-transform: uppercase;
|
||||
font: $font_18_semibold;
|
||||
text-align: center;
|
||||
color: $color_primary;
|
||||
|
||||
svg {
|
||||
fill: $color_primary;
|
||||
}
|
||||
}
|
|
@ -1,8 +1,6 @@
|
|||
import React, { FC, useCallback, useMemo, useState } from 'react';
|
||||
|
||||
import { apiRestoreCode } from '~/api/auth';
|
||||
import { RestoreInvalidCode } from '~/components/auth/restore/RestoreInvalidCode';
|
||||
import { RestoreSuccess } from '~/components/auth/restore/RestoreSuccess';
|
||||
import { Group } from '~/components/containers/Group';
|
||||
import { Button } from '~/components/input/Button';
|
||||
import { InputText } from '~/components/input/InputText';
|
||||
|
@ -13,13 +11,18 @@ import { DialogComponentProps } from '~/types/modal';
|
|||
|
||||
import { BetterScrollDialog } from '../../../components/dialogs/BetterScrollDialog';
|
||||
|
||||
import { RestoreInvalidCode } from './components/RestoreInvalidCode';
|
||||
import { RestoreSuccess } from './components/RestoreSuccess';
|
||||
import styles from './styles.module.scss';
|
||||
|
||||
type RestorePasswordDialogProps = DialogComponentProps & {
|
||||
code: string;
|
||||
};
|
||||
|
||||
const RestorePasswordDialog: FC<RestorePasswordDialogProps> = ({ onRequestClose, code }) => {
|
||||
const RestorePasswordDialog: FC<RestorePasswordDialogProps> = ({
|
||||
onRequestClose,
|
||||
code,
|
||||
}) => {
|
||||
useCloseOnEscape(onRequestClose);
|
||||
|
||||
const { codeUser, isLoading, error } = useRestoreCode(code);
|
||||
|
@ -30,7 +33,7 @@ const RestorePasswordDialog: FC<RestorePasswordDialogProps> = ({ onRequestClose,
|
|||
const { handleChange, handleSubmit, values, errors } = useRestorePasswordForm(
|
||||
code,
|
||||
apiRestoreCode,
|
||||
onSent
|
||||
onSent,
|
||||
);
|
||||
|
||||
const buttons = useMemo(
|
||||
|
@ -39,12 +42,17 @@ const RestorePasswordDialog: FC<RestorePasswordDialogProps> = ({ onRequestClose,
|
|||
<Button color="primary">Восстановить</Button>
|
||||
</Group>
|
||||
),
|
||||
[]
|
||||
[],
|
||||
);
|
||||
|
||||
const overlay = useMemo(() => {
|
||||
if (isSent) {
|
||||
return <RestoreSuccess username={codeUser?.username} onClick={onRequestClose} />;
|
||||
return (
|
||||
<RestoreSuccess
|
||||
username={codeUser?.username}
|
||||
onClick={onRequestClose}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
if (error) {
|
||||
|
@ -67,7 +75,9 @@ const RestorePasswordDialog: FC<RestorePasswordDialogProps> = ({ onRequestClose,
|
|||
>
|
||||
<div className={styles.wrap}>
|
||||
<Group>
|
||||
<div className={styles.header}>Пришло время сменить пароль, {codeUser?.username}</div>
|
||||
<div className={styles.header}>
|
||||
Пришло время сменить пароль, {codeUser?.username}
|
||||
</div>
|
||||
|
||||
<InputText
|
||||
title="Новый пароль"
|
||||
|
@ -89,8 +99,8 @@ const RestorePasswordDialog: FC<RestorePasswordDialogProps> = ({ onRequestClose,
|
|||
<Group className={styles.text}>
|
||||
<p>Новый пароль должен быть не короче 6 символов.</p>
|
||||
<p>
|
||||
Вряд ли кто-нибудь будет пытаться нас взломать, но сложный пароль всегда лучше
|
||||
простого.
|
||||
Вряд ли кто-нибудь будет пытаться нас взломать, но сложный
|
||||
пароль всегда лучше простого.
|
||||
</p>
|
||||
</Group>
|
||||
</Group>
|
||||
|
|
|
@ -0,0 +1,25 @@
|
|||
import React, { VFC } from 'react';
|
||||
|
||||
import { Group } from '~/components/containers/Group';
|
||||
import { Button } from '~/components/input/Button';
|
||||
import { Icon } from '~/components/input/Icon';
|
||||
|
||||
import styles from './styles.module.scss';
|
||||
|
||||
interface RestoreSentProps {
|
||||
onClose: () => void;
|
||||
}
|
||||
|
||||
const RestoreSent: VFC<RestoreSentProps> = ({ onClose }) => (
|
||||
<Group className={styles.shade}>
|
||||
<Icon icon="check" size={64} />
|
||||
|
||||
<div>Проверьте почту, мы отправили на неё код</div>
|
||||
|
||||
<div />
|
||||
|
||||
<Button onClick={onClose}>Отлично!</Button>
|
||||
</Group>
|
||||
);
|
||||
|
||||
export { RestoreSent };
|
|
@ -0,0 +1,27 @@
|
|||
@import 'src/styles/variables';
|
||||
|
||||
.shade {
|
||||
@include outer_shadow();
|
||||
|
||||
background: $content_bg;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
z-index: 1;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
border-radius: $radius;
|
||||
padding: $gap * 2;
|
||||
box-sizing: border-box;
|
||||
text-transform: uppercase;
|
||||
font: $font_18_semibold;
|
||||
text-align: center;
|
||||
color: $color_primary;
|
||||
|
||||
svg {
|
||||
fill: $color_primary;
|
||||
}
|
||||
}
|
|
@ -1,7 +1,6 @@
|
|||
import React, { useCallback, useMemo, useState, VFC } from 'react';
|
||||
|
||||
import { apiRequestRestoreCode } from '~/api/auth';
|
||||
import { RestoreSent } from '~/components/auth/restore/RestoreSent';
|
||||
import { Group } from '~/components/containers/Group';
|
||||
import { Button } from '~/components/input/Button';
|
||||
import { InputText } from '~/components/input/InputText';
|
||||
|
@ -11,6 +10,7 @@ import { DialogComponentProps } from '~/types/modal';
|
|||
|
||||
import { BetterScrollDialog } from '../../../components/dialogs/BetterScrollDialog';
|
||||
|
||||
import { RestoreSent } from './components/RestoreSent';
|
||||
import styles from './styles.module.scss';
|
||||
|
||||
interface RestoreRequestDialogProps extends DialogComponentProps {}
|
||||
|
|
|
@ -7,8 +7,8 @@ import { Button } from '~/components/input/Button';
|
|||
import { useTelegramAccount } from '~/hooks/auth/useTelegramAccount';
|
||||
import { DialogComponentProps } from '~/types/modal';
|
||||
|
||||
import { TelegramLoginForm } from '../../../components/auth/oauth/TelegramLoginForm/index';
|
||||
import { BetterScrollDialog } from '../../../components/dialogs/BetterScrollDialog';
|
||||
import { TelegramLoginForm } from '../LoginDialog/components/TelegramLoginForm/index';
|
||||
|
||||
interface TelegramAttachDialogProps extends DialogComponentProps {}
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue