web: split components into hooks & files

This commit is contained in:
vchikalkin 2024-07-14 15:26:32 +03:00
parent 099fe2646e
commit 60519a171a
14 changed files with 227 additions and 187 deletions

View File

@ -7,59 +7,3 @@
margin: 7px 0;
}
}
.button-submit {
display: flex;
justify-content: center;
align-items: center;
}
.button-telegram {
@extend .button-submit;
display: flex;
flex-direction: row;
justify-content: center;
align-items: center;
text-transform: none;
text-overflow: ellipsis;
white-space: nowrap;
overflow: hidden;
animation: colorTransition 1s cubic-bezier(0.16, 1, 0.3, 1) forwards;
}
.button-telegram {
img {
margin: 0;
margin-right: 10px;
}
}
@keyframes colorTransition {
0% {
background-color: var(--color-primary);
}
100% {
background-color: #54a9eb;
}
}
.button-telegram-icon {
filter: brightness(0) invert(1);
margin: 0 !important;
margin-right: 13px !important;
margin-left: none !important;
}
.spinner-icon {
filter: brightness(0) invert(1);
fill: var(--color-primary);
margin: 0 !important;
margin-right: 6px !important;
}
.loading-wrapper {
display: flex;
justify-content: center;
align-items: center;
}

View File

@ -1,5 +1,5 @@
import styles from './Form.module.scss';
import type { FormData, FormProps } from './types';
import type { FormData, FormProps } from './lib/types';
import { publicRuntimeConfig } from '@/config/runtime';
import { FormStateContext } from '@/context/form-state';
import type { PropsWithChildren } from 'react';

View File

@ -1,48 +1,22 @@
import { BaseForm } from './base-form';
import { ButtonLoading, ButtonLogin } from './buttons';
import { ERROR_INVALID_CREDENTIALS, ERROR_SERVER } from './errors';
import type { FormData } from './types';
import { redirect } from '@/components/Form/utils';
import { useLogin } from './hooks/default';
import { useRefreshToken } from './hooks/token';
import { ButtonLoading, ButtonLogin } from './lib/buttons';
import { FormStateContext } from '@/context/form-state';
import axios from 'axios';
import { useContext } from 'react';
export function DefaultForm() {
useRefreshToken();
const { handleLogin } = useLogin();
const {
dispatch,
state: { step, user },
} = useContext(FormStateContext);
function handleRefreshToken() {
axios
.get('/refresh-token')
.then(() => redirect())
.catch(() =>
dispatch({
payload: { error: ERROR_SERVER, user: undefined },
type: 'set-error',
})
);
}
if (step === 'login' && user) {
handleRefreshToken();
return <ButtonLoading />;
}
function handleLogin(data: FormData) {
return axios
.post('/login', data)
.then(() => redirect())
.catch(() =>
dispatch({
payload: { error: ERROR_INVALID_CREDENTIALS },
type: 'set-error',
})
);
}
return (
<BaseForm onSubmit={(data) => handleLogin(data)}>
<ButtonLogin />

View File

@ -0,0 +1,24 @@
import type { FormData } from '../lib/types';
import { redirect } from '@/components/Form/lib/utils';
import { ERROR_INVALID_CREDENTIALS } from '@/constants/errors';
import { FormStateContext } from '@/context/form-state';
import axios from 'axios';
import { useContext } from 'react';
export function useLogin() {
const { dispatch } = useContext(FormStateContext);
function handleLogin(data: FormData) {
return axios
.post('/login', data)
.then(() => redirect())
.catch(() =>
dispatch({
payload: { error: ERROR_INVALID_CREDENTIALS },
type: 'set-error',
})
);
}
return { handleLogin };
}

View File

@ -0,0 +1,2 @@
export * from './socket';
export * from './token';

View File

@ -0,0 +1,99 @@
import type { FormData } from '../lib/types';
import { useSocket } from './socket';
import { redirect } from '@/components/Form/lib/utils';
import { ERROR_INVALID_CREDENTIALS, ERROR_SERVER } from '@/constants/errors';
import { FormStateContext } from '@/context/form-state';
import type { TelegramUrlResponse } from '@/types/error';
import type { LdapUser } from '@/types/user';
import axios, { isAxiosError } from 'axios';
import { useContext, useEffect } from 'react';
export function useLogin() {
const { dispatch } = useContext(FormStateContext);
function handleLogin(data: FormData) {
axios
.post<LdapUser>('/login', data)
.then((res) => {
dispatch({
payload: {
step: 'telegram',
user: res.data,
},
type: 'set-step',
});
})
.catch(() =>
dispatch({
payload: { error: ERROR_INVALID_CREDENTIALS },
type: 'set-error',
})
);
}
return { handleLogin };
}
export function useTelegramLogin() {
const { dispatch } = useContext(FormStateContext);
function handleTelegramLogin() {
axios
.post<LdapUser>('/login-telegram')
.then(() => {
dispatch({
payload: {
step: 'telegram-login',
},
type: 'set-step',
});
})
.catch((error_) => {
let error = ERROR_SERVER;
if (isAxiosError<TelegramUrlResponse>(error_) && error_.response?.data?.message) {
error = error_.response?.data?.message;
}
return dispatch({
payload: { error },
type: 'set-error',
});
});
}
return { handleTelegramLogin };
}
export function useTelegramConfirm() {
const {
dispatch,
state: { step },
} = 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]);
}

View File

@ -0,0 +1,28 @@
import { redirect } from '@/components/Form/lib/utils';
import { ERROR_SERVER } from '@/constants/errors';
import { FormStateContext } from '@/context/form-state';
import axios from 'axios';
import { useContext, useEffect } from 'react';
export function useRefreshToken() {
const {
dispatch,
state: { step, user },
} = useContext(FormStateContext);
function handleRefreshToken() {
axios
.get('/refresh-token')
.then(() => redirect())
.catch(() =>
dispatch({
payload: { error: ERROR_SERVER, user: undefined },
type: 'set-error',
})
);
}
useEffect(() => {
if (step === 'login' && user) handleRefreshToken();
}, []);
}

View File

@ -0,0 +1,55 @@
.button-submit {
display: flex;
justify-content: center;
align-items: center;
}
.button-telegram {
@extend .button-submit;
display: flex;
flex-direction: row;
justify-content: center;
align-items: center;
text-transform: none;
text-overflow: ellipsis;
white-space: nowrap;
overflow: hidden;
animation: colorTransition 1s cubic-bezier(0.16, 1, 0.3, 1) forwards;
}
.button-telegram {
img {
margin: 0;
margin-right: 10px;
}
}
@keyframes colorTransition {
0% {
background-color: var(--color-primary);
}
100% {
background-color: #54a9eb;
}
}
.button-telegram-icon {
filter: brightness(0) invert(1);
margin: 0 !important;
margin-right: 13px !important;
margin-left: none !important;
}
.spinner-icon {
filter: brightness(0) invert(1);
fill: var(--color-primary);
margin: 0 !important;
margin-right: 6px !important;
}
.loading-wrapper {
display: flex;
justify-content: center;
align-items: center;
}

View File

@ -1,6 +1,6 @@
import Spinner from '../../public/assets/animated/90-ring.svg';
import TelegramIcon from '../../public/assets/images/telegram.svg?url';
import styles from './Form.module.scss';
import styles from './buttons.module.scss';
import Spinner from '@/public/assets/animated/90-ring.svg';
import TelegramIcon from '@/public/assets/images/telegram.svg?url';
import Image from 'next/image';
import type { ButtonHTMLAttributes } from 'react';

View File

@ -1,107 +1,21 @@
import { BaseForm } from './base-form';
import { ButtonLoading, ButtonLogin, ButtonTelegram, ButtonTelegramLogin } from './buttons';
import { ERROR_INVALID_CREDENTIALS, ERROR_SERVER } from './errors';
import type { FormData } from './types';
import { redirect } from '@/components/Form/utils';
import { useLogin, useTelegramConfirm, useTelegramLogin } from './hooks/telegram';
import { useRefreshToken } from './hooks/token';
import { ButtonLoading, ButtonLogin, ButtonTelegram, ButtonTelegramLogin } from './lib/buttons';
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 { useContext, useEffect } from 'react';
import { useContext } from 'react';
export function TelegramForm() {
useRefreshToken();
const { handleLogin } = useLogin();
const { handleTelegramLogin } = useTelegramLogin();
useTelegramConfirm();
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<LdapUser>('/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<LdapUser>('/login-telegram')
.then(() => {
dispatch({
payload: {
step: 'telegram-login',
},
type: 'set-step',
});
})
.catch((error_) => {
let error = ERROR_SERVER;
if (isAxiosError<TelegramUrlResponse>(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) {
handleRefreshToken();
return <ButtonLoading />;
}