diff --git a/apps/web/components/Form.tsx b/apps/web/components/Form.tsx deleted file mode 100644 index 5027502..0000000 --- a/apps/web/components/Form.tsx +++ /dev/null @@ -1,268 +0,0 @@ -/* eslint-disable react/jsx-curly-newline */ -/* eslint-disable sonarjs/no-small-switch */ -import TelegramIcon from '../public/assets/images/telegram.svg'; -import styles from './Form.module.scss'; -import { publicRuntimeConfig } from '@/config/runtime'; -import { FormStateContext } from '@/context/form-state'; -import { useSocket } from '@/hooks/socket'; -import type { TelegramUrlResponse } from '@/types/error'; -import type { LdapUser } from '@/types/user'; -import axios, { isAxiosError } from 'axios'; -import Image from 'next/image'; -import type { PropsWithChildren } from 'react'; -import { useContext, useEffect } from 'react'; -import { useForm } from 'react-hook-form'; - -const ERROR_INVALID_CREDENTIALS = 'Неверный логин или пароль'; -const ERROR_SERVER = 'Не удалось войти. Повторите попытку позже'; - -const { APP_BASE_PATH, TELEGRAM_BOT_URL } = publicRuntimeConfig; - -type FormData = { - readonly login: string; - readonly password: string; -}; - -type FormProps = { - readonly onSubmit: (data: FormData) => void; -}; - -function redirect() { - const redirectUrl = - (window.location.pathname.replace(APP_BASE_PATH, '') || '/') + (window.location.search || ''); - - window.location.replace(redirectUrl); -} - -function BaseForm({ children, onSubmit }: FormProps & PropsWithChildren) { - const { handleSubmit, register } = useForm(); - const { - state: { error, step }, - } = useContext(FormStateContext); - - return ( -
- - - {step === 'telegram-login' ? ( - - Открыть чат с ботом - - ) : null} - {error ? {error} : null} - {children} -
- ); -} - -export const Form = { - Default() { - const { - dispatch, - state: { step, user }, - } = useContext(FormStateContext); - - function handleRefreshToken() { - axios - .get('/refresh-token') - .then(() => redirect()) - .catch(() => - dispatch({ - payload: { error: ERROR_SERVER }, - type: 'set-error', - }) - ); - } - - if (step === 'login' && user) { - return ( - - ); - } - - function handleLogin(data: FormData) { - return axios - .post('/login', data) - .then(() => redirect()) - .catch(() => - dispatch({ - payload: { error: ERROR_INVALID_CREDENTIALS }, - type: 'set-error', - }) - ); - } - - return ( - handleLogin(data)}> - - - ); - }, - - Telegram() { - const { - dispatch, - state: { step, user }, - } = useContext(FormStateContext); - - const { socket } = useSocket(); - - useEffect(() => { - if (step === 'telegram-login') { - socket.open(); - socket.on('connect', () => {}); - - socket.on('auth-allow', () => { - socket.off('connect'); - axios - .get('/login-confirm') - .then(() => redirect()) - .catch(() => - dispatch({ - payload: { error: ERROR_SERVER }, - type: 'set-error', - }) - ); - }); - } - - return () => { - socket.off('connect'); - }; - }, [dispatch, socket, step]); - - function handleLogin(data: FormData) { - axios - .post('/login', data) - .then((res) => { - dispatch({ - payload: { - step: 'telegram', - user: res.data, - }, - type: 'set-step', - }); - }) - .catch(() => - dispatch({ - payload: { error: ERROR_INVALID_CREDENTIALS }, - type: 'set-error', - }) - ); - } - - function handleTelegramLogin() { - axios - .post('/login-telegram') - .then(() => { - dispatch({ - payload: { - step: 'telegram-login', - }, - type: 'set-step', - }); - }) - .catch((error_) => { - let error = ERROR_SERVER; - - if (isAxiosError(error_) && error_.response?.data?.message) { - error = error_.response?.data?.message; - } - - return dispatch({ - payload: { error }, - type: 'set-error', - }); - }); - } - - // eslint-disable-next-line sonarjs/no-identical-functions - function handleRefreshToken() { - axios - .get('/refresh-token') - .then(() => redirect()) - .catch(() => - dispatch({ - payload: { error: ERROR_SERVER }, - type: 'set-error', - }) - ); - } - - if (step === 'login' && user) { - return ( - - ); - } - - if (step === 'telegram') { - return ( - handleTelegramLogin()}> - - - ); - } - - if (step === 'telegram-login') { - return ( - {}}> - - - ); - } - - return ( - handleLogin(data)}> - - - ); - }, -}; diff --git a/apps/web/components/Form.module.scss b/apps/web/components/Form/Form.module.scss similarity index 100% rename from apps/web/components/Form.module.scss rename to apps/web/components/Form/Form.module.scss diff --git a/apps/web/components/Form/base-form.tsx b/apps/web/components/Form/base-form.tsx new file mode 100644 index 0000000..cb3ea5a --- /dev/null +++ b/apps/web/components/Form/base-form.tsx @@ -0,0 +1,44 @@ +import styles from './Form.module.scss'; +import type { FormData, FormProps } from './types'; +import { publicRuntimeConfig } from '@/config/runtime'; +import { FormStateContext } from '@/context/form-state'; +import type { PropsWithChildren } from 'react'; +import { useContext } from 'react'; +import { useForm } from 'react-hook-form'; + +const { TELEGRAM_BOT_URL } = publicRuntimeConfig; + +export function BaseForm({ children, onSubmit }: FormProps & PropsWithChildren) { + const { handleSubmit, register } = useForm(); + const { + state: { error, step }, + } = useContext(FormStateContext); + + return ( +
+ + + {step === 'telegram-login' ? ( + + Открыть чат с ботом + + ) : null} + {error ? {error} : null} + {children} +
+ ); +} diff --git a/apps/web/components/Form/default-form.tsx b/apps/web/components/Form/default-form.tsx new file mode 100644 index 0000000..b20760b --- /dev/null +++ b/apps/web/components/Form/default-form.tsx @@ -0,0 +1,59 @@ +import { BaseForm } from './base-form'; +import { ERROR_INVALID_CREDENTIALS, ERROR_SERVER } from './errors'; +import styles from './Form.module.scss'; +import type { FormData } from './types'; +import { FormStateContext } from '@/context/form-state'; +import { redirect } from '@/utils/router'; +import axios from 'axios'; +import { useContext } from 'react'; + +export function DefaultForm() { + const { + dispatch, + state: { step, user }, + } = useContext(FormStateContext); + + function handleRefreshToken() { + axios + .get('/refresh-token') + .then(() => redirect()) + .catch(() => + dispatch({ + payload: { error: ERROR_SERVER }, + type: 'set-error', + }) + ); + } + + if (step === 'login' && user) { + return ( + + ); + } + + function handleLogin(data: FormData) { + return axios + .post('/login', data) + .then(() => redirect()) + .catch(() => + dispatch({ + payload: { error: ERROR_INVALID_CREDENTIALS }, + type: 'set-error', + }) + ); + } + + return ( + handleLogin(data)}> + + + ); +} diff --git a/apps/web/components/Form/errors.ts b/apps/web/components/Form/errors.ts new file mode 100644 index 0000000..ca53a64 --- /dev/null +++ b/apps/web/components/Form/errors.ts @@ -0,0 +1,2 @@ +export const ERROR_INVALID_CREDENTIALS = 'Неверный логин или пароль'; +export const ERROR_SERVER = 'Не удалось войти. Повторите попытку позже'; diff --git a/apps/web/components/Form/index.tsx b/apps/web/components/Form/index.tsx new file mode 100644 index 0000000..03f08ca --- /dev/null +++ b/apps/web/components/Form/index.tsx @@ -0,0 +1,2 @@ +export * from './default-form'; +export * from './telegram-form'; diff --git a/apps/web/components/Form/telegram-form.tsx b/apps/web/components/Form/telegram-form.tsx new file mode 100644 index 0000000..2ca9ab1 --- /dev/null +++ b/apps/web/components/Form/telegram-form.tsx @@ -0,0 +1,157 @@ +import TelegramIcon from '../../public/assets/images/telegram.svg'; +import { BaseForm } from './base-form'; +import { ERROR_INVALID_CREDENTIALS, ERROR_SERVER } from './errors'; +import styles from './Form.module.scss'; +import type { FormData } from './types'; +import { FormStateContext } from '@/context/form-state'; +import { useSocket } from '@/hooks/socket'; +import type { TelegramUrlResponse } from '@/types/error'; +import type { LdapUser } from '@/types/user'; +import { redirect } from '@/utils/router'; +import axios, { isAxiosError } from 'axios'; +import Image from 'next/image'; +import { useContext, useEffect } from 'react'; + +export function TelegramForm() { + const { + dispatch, + state: { step, user }, + } = useContext(FormStateContext); + + const { socket } = useSocket(); + + useEffect(() => { + if (step === 'telegram-login') { + socket.open(); + socket.on('connect', () => {}); + + socket.on('auth-allow', () => { + socket.off('connect'); + axios + .get('/login-confirm') + .then(() => redirect()) + .catch(() => + dispatch({ + payload: { error: ERROR_SERVER }, + type: 'set-error', + }) + ); + }); + } + + return () => { + socket.off('connect'); + }; + }, [dispatch, socket, step]); + + function handleLogin(data: FormData) { + axios + .post('/login', data) + .then((res) => { + dispatch({ + payload: { + step: 'telegram', + user: res.data, + }, + type: 'set-step', + }); + }) + .catch(() => + dispatch({ + payload: { error: ERROR_INVALID_CREDENTIALS }, + type: 'set-error', + }) + ); + } + + function handleTelegramLogin() { + axios + .post('/login-telegram') + .then(() => { + dispatch({ + payload: { + step: 'telegram-login', + }, + type: 'set-step', + }); + }) + .catch((error_) => { + let error = ERROR_SERVER; + + if (isAxiosError(error_) && error_.response?.data?.message) { + error = error_.response?.data?.message; + } + + return dispatch({ + payload: { error }, + type: 'set-error', + }); + }); + } + + function handleRefreshToken() { + axios + .get('/refresh-token') + .then(() => redirect()) + .catch(() => + dispatch({ + payload: { error: ERROR_SERVER }, + type: 'set-error', + }) + ); + } + + if (step === 'login' && user) { + return ( + + ); + } + + if (step === 'telegram') { + return ( + handleTelegramLogin()}> + + + ); + } + + if (step === 'telegram-login') { + return ( + {}}> + + + ); + } + + return ( + handleLogin(data)}> + + + ); +} diff --git a/apps/web/components/Form/types.tsx b/apps/web/components/Form/types.tsx new file mode 100644 index 0000000..3bede27 --- /dev/null +++ b/apps/web/components/Form/types.tsx @@ -0,0 +1,7 @@ +export type FormData = { + readonly login: string; + readonly password: string; +}; +export type FormProps = { + readonly onSubmit: (data: FormData) => void; +}; diff --git a/apps/web/components/Login.jsx b/apps/web/components/Login.jsx index 6e51fa9..270abfb 100644 --- a/apps/web/components/Login.jsx +++ b/apps/web/components/Login.jsx @@ -1,4 +1,4 @@ -import { Form } from './Form'; +import * as Form from './Form'; import styles from './Login.module.scss'; import { Logo } from '@/elements'; @@ -7,7 +7,7 @@ export function Login({ tfa }) {
- {tfa ? : } + {tfa ? : }
); diff --git a/apps/web/utils/router.ts b/apps/web/utils/router.ts new file mode 100644 index 0000000..ad25982 --- /dev/null +++ b/apps/web/utils/router.ts @@ -0,0 +1,10 @@ +import { publicRuntimeConfig } from '@/config/runtime'; + +const { APP_BASE_PATH } = publicRuntimeConfig; + +export function redirect() { + const redirectUrl = + (window.location.pathname.replace(APP_BASE_PATH, '') || '/') + (window.location.search || ''); + + window.location.replace(redirectUrl); +}