Ott 1500 remove authentication pages (#449)
* fix(1500): DateFilter key fix * refactor(1500): removed Login page * refactor(1500): removed Registration page * fix(1500): redirect to auth provider if no tokenkeep-around/af30b88d367751c9e05a735e4a0467a96238ef47
parent
6945f318a4
commit
92d4d6be4c
@ -1,16 +0,0 @@ |
||||
import { useEffect } from 'react' |
||||
|
||||
import { useAuthStore } from 'features/AuthStore' |
||||
import { queryParamStorage } from 'features/QueryParamsStorage' |
||||
|
||||
export const RedirectCallback = () => { |
||||
const { signinRedirectCallback } = useAuthStore() |
||||
|
||||
useEffect(() => { |
||||
signinRedirectCallback().then(() => { |
||||
queryParamStorage.clear() |
||||
}) |
||||
}, [signinRedirectCallback]) |
||||
|
||||
return null |
||||
} |
||||
@ -1,76 +0,0 @@ |
||||
import { lazy } from 'react' |
||||
|
||||
import { |
||||
Redirect, |
||||
Route, |
||||
Switch, |
||||
} from 'react-router-dom' |
||||
|
||||
import styled, { css } from 'styled-components/macro' |
||||
|
||||
import { PAGES } from 'config' |
||||
import { publicLexics } from 'config/lexics/public' |
||||
|
||||
import { useLexicsConfig } from 'features/LexicsStore' |
||||
import { LanguageSelect } from 'features/LanguageSelect' |
||||
import { HeaderGroup } from 'features/ProfileHeader/styled' |
||||
import { isMobileDevice } from 'config/userAgent' |
||||
|
||||
import { RedirectCallback } from './RedirectCallback' |
||||
|
||||
const Login = lazy(() => import('features/Login')) |
||||
const Register = lazy(() => import('features/Register')) |
||||
|
||||
const Main = styled.main` |
||||
width: 100%; |
||||
${isMobileDevice |
||||
? css` |
||||
@media screen and (orientation: landscape){ |
||||
min-height: 100vh; |
||||
} |
||||
` |
||||
: ''}; |
||||
` |
||||
|
||||
const HeaderStyled = styled.header` |
||||
display: flex; |
||||
justify-content: space-between; |
||||
height: 3.02rem; |
||||
padding: 0.755rem 1.369rem 0 1.04rem; |
||||
margin-bottom: 1.416rem; |
||||
${isMobileDevice |
||||
? css` |
||||
padding-top: 15px; |
||||
` |
||||
: ''}; |
||||
` |
||||
|
||||
export const UnauthenticatedApp = () => { |
||||
useLexicsConfig(publicLexics) |
||||
|
||||
return ( |
||||
<Main> |
||||
<HeaderStyled> |
||||
<HeaderGroup /> |
||||
<HeaderGroup> |
||||
<LanguageSelect /> |
||||
</HeaderGroup> |
||||
</HeaderStyled> |
||||
<Switch> |
||||
<Route path={PAGES.login}> |
||||
<Login /> |
||||
</Route> |
||||
|
||||
<Route path={PAGES.register}> |
||||
<Register /> |
||||
</Route> |
||||
|
||||
<Route path='/redirect'> |
||||
<RedirectCallback /> |
||||
</Route> |
||||
|
||||
<Redirect to={PAGES.login} /> |
||||
</Switch> |
||||
</Main> |
||||
) |
||||
} |
||||
@ -1,18 +0,0 @@ |
||||
import { useHistory } from 'react-router-dom' |
||||
|
||||
import { PAGES } from 'config' |
||||
import { register } from 'requests' |
||||
|
||||
type Args = Parameters<typeof register>[0] |
||||
|
||||
export const useRegister = () => { |
||||
const history = useHistory() |
||||
|
||||
const goToRegistrationSuccess = () => { |
||||
history.replace(`${PAGES.register}/successful`) |
||||
} |
||||
|
||||
return async (args: Args) => ( |
||||
register(args).then(goToRegistrationSuccess) |
||||
) |
||||
} |
||||
@ -1,66 +0,0 @@ |
||||
import map from 'lodash/map' |
||||
|
||||
import { useLexicsStore } from 'features/LexicsStore' |
||||
import { OutsideClick } from 'features/OutsideClick' |
||||
|
||||
import { useToggle } from 'hooks' |
||||
|
||||
import type { Languages } from './config' |
||||
import { langsList } from './config' |
||||
import { |
||||
Wrapper, |
||||
WorldIcon, |
||||
LangsList, |
||||
LangsItem, |
||||
FlagIcon, |
||||
} from './styled' |
||||
|
||||
export const LanguageSelect = () => { |
||||
const { changeLang, translate } = useLexicsStore() |
||||
const { |
||||
close, |
||||
isOpen, |
||||
open, |
||||
} = useToggle() |
||||
|
||||
const handleLangChange = (locale: Languages) => () => { |
||||
changeLang(locale) |
||||
close() |
||||
} |
||||
|
||||
return ( |
||||
<Wrapper> |
||||
<WorldIcon |
||||
onClick={open} |
||||
active={isOpen} |
||||
title={translate('select_language')} |
||||
aria-expanded={isOpen} |
||||
aria-controls='langsList' |
||||
/> |
||||
{isOpen && ( |
||||
<OutsideClick onClick={close}> |
||||
<LangsList id='langsList'> |
||||
{ |
||||
map( |
||||
langsList, |
||||
({ |
||||
className, |
||||
locale, |
||||
title, |
||||
}) => ( |
||||
<LangsItem |
||||
key={locale} |
||||
title={title} |
||||
onClick={handleLangChange(locale)} |
||||
> |
||||
<FlagIcon flag={className} /> |
||||
</LangsItem> |
||||
), |
||||
) |
||||
} |
||||
</LangsList> |
||||
</OutsideClick> |
||||
)} |
||||
</Wrapper> |
||||
) |
||||
} |
||||
@ -1,145 +0,0 @@ |
||||
import styled, { css } from 'styled-components/macro' |
||||
import { isMobileDevice } from 'config/userAgent' |
||||
|
||||
export const Wrapper = styled.div` |
||||
position: relative; |
||||
display: flex; |
||||
align-items: center; |
||||
` |
||||
|
||||
type WorldIconProps = { |
||||
active?: boolean, |
||||
} |
||||
|
||||
export const WorldIcon = styled.button<WorldIconProps>` |
||||
display: block; |
||||
width: 0.85rem; |
||||
height: 0.85rem; |
||||
border: none; |
||||
background: none; |
||||
outline: none; |
||||
background-image: url(/images/worldIcon.svg); |
||||
background-repeat: no-repeat; |
||||
cursor: pointer; |
||||
${isMobileDevice |
||||
? css` |
||||
background-size: cover; |
||||
width: 19px; |
||||
min-width: 19px; |
||||
height: 19px; |
||||
position: absolute; |
||||
top: 5px; |
||||
right: 10px; |
||||
` |
||||
: ''}; |
||||
|
||||
:hover { |
||||
opacity: 1; |
||||
} |
||||
|
||||
${({ active }) => (active ? 'opacity: 1;' : 'opacity: 0.7;')} |
||||
` |
||||
|
||||
export const LangsList = styled.ul` |
||||
display: flex; |
||||
flex-wrap: wrap; |
||||
width: 4.6rem; |
||||
position: absolute; |
||||
background-color: #666666; |
||||
box-shadow: 0 2px 5px rgba(0, 0, 0, 0.5); |
||||
border-radius: 2px; |
||||
right: -92%; |
||||
top: calc(100% + 0.19rem); |
||||
${isMobileDevice |
||||
? css` |
||||
top: 35px; |
||||
right: 10px; |
||||
width: 55px; |
||||
height: 25px; |
||||
justify-content: center; |
||||
align-items: center; |
||||
gap: 5px; |
||||
` |
||||
: ''} |
||||
:before { |
||||
content: ''; |
||||
width: 0.567rem; |
||||
height: 0.567rem; |
||||
transform: translateY(-50%) rotate(45deg); |
||||
position: absolute; |
||||
right: 0.9rem; |
||||
background-color: #666666; |
||||
z-index: 0; |
||||
${isMobileDevice |
||||
? css` |
||||
width: 10px; |
||||
height: 20px; |
||||
right: 10px; |
||||
` |
||||
: ''} |
||||
} |
||||
` |
||||
|
||||
export const LangsItem = styled.li` |
||||
text-align: center; |
||||
display: flex; |
||||
align-items: center; |
||||
justify-content: center; |
||||
width: 2.28rem; |
||||
height: 2.28rem; |
||||
z-index: 1; |
||||
border-radius: 2px; |
||||
${isMobileDevice |
||||
? css` |
||||
width: 22px; |
||||
` |
||||
: ''} |
||||
:hover { |
||||
background-color: #999999; |
||||
cursor: pointer; |
||||
} |
||||
` |
||||
|
||||
type Position = { |
||||
col: number, |
||||
row: number, |
||||
} |
||||
|
||||
const flagPositions: Record<string, Position> = { |
||||
gb: { |
||||
col: 4, |
||||
row: 12, |
||||
}, |
||||
ru: { |
||||
col: 8, |
||||
row: 9, |
||||
}, |
||||
} |
||||
|
||||
const getFlagPosition = (flag: string) => { |
||||
const { col, row } = flagPositions[flag] |
||||
return `${col * -24}px ${row * -16}px` |
||||
} |
||||
|
||||
type FlagIconProps = { |
||||
flag: string, |
||||
} |
||||
|
||||
export const FlagIcon = styled.span<FlagIconProps>` |
||||
display: inline-block; |
||||
width: 28px; |
||||
height: 16px; |
||||
transition: 0.3s; |
||||
|
||||
background-image: url('/images/flags-sprite.png'); |
||||
background-position: 1000px 1000px; |
||||
background-repeat: no-repeat; |
||||
display: inline-block; |
||||
position: relative; |
||||
|
||||
background-size: 360px; |
||||
height: 16px; |
||||
width: 24px; |
||||
|
||||
background-position: ${({ flag }) => getFlagPosition(flag)}; |
||||
` |
||||
@ -1,74 +0,0 @@ |
||||
import styled, { css } from 'styled-components/macro' |
||||
|
||||
import { isMobileDevice } from 'config/userAgent' |
||||
|
||||
import { ButtonSolid as BaseButtonSolid } from 'features/Common' |
||||
|
||||
const backgroundColors = { |
||||
facebook: '#4468B2', |
||||
google: '#FFFFFF', |
||||
} |
||||
|
||||
const textColors = { |
||||
facebook: '#FFFFFF', |
||||
google: '#4F4F4F', |
||||
} |
||||
|
||||
const Button = styled(BaseButtonSolid)<Props>` |
||||
display: flex; |
||||
justify-content: center; |
||||
align-items: center; |
||||
width: 100%; |
||||
border-radius: 10px; |
||||
box-shadow: 0px 1px 0.283rem rgba(0, 0, 0, 0.2); |
||||
font-weight: 500; |
||||
font-size: 0.95rem; |
||||
letter-spacing: -0.408px; |
||||
|
||||
color: ${({ provider }) => textColors[provider]}; |
||||
background-color: ${({ provider }) => backgroundColors[provider]}; |
||||
margin-bottom: 0.71rem; |
||||
${isMobileDevice |
||||
? css` |
||||
border-radius: 6px; |
||||
font-size: 12.31px; |
||||
margin-bottom: 9.23px; |
||||
@media screen and (orientation: landscape) { |
||||
max-width: 290px; |
||||
} |
||||
` |
||||
: ''} |
||||
|
||||
` |
||||
|
||||
const Icon = styled.span<Props>` |
||||
display: inline-block; |
||||
width: 1rem; |
||||
height: 1rem; |
||||
margin-right: 0.38rem; |
||||
background-image: url(/images/${({ provider }) => provider}.png); |
||||
background-size: contain; |
||||
background-repeat: no-repeat; |
||||
${isMobileDevice |
||||
? css` |
||||
width: 12.92px; |
||||
height: 12.92px; |
||||
` |
||||
: ''} |
||||
` |
||||
|
||||
const lexics = { |
||||
facebook: 'Войти через Facebook', |
||||
google: 'Войти через Google', |
||||
} |
||||
|
||||
type Props = { |
||||
provider: keyof typeof backgroundColors, |
||||
} |
||||
|
||||
export const AuthProviderButton = ({ provider }: Props) => ( |
||||
<Button type='button' provider={provider}> |
||||
<Icon provider={provider} /> |
||||
{lexics[provider]} |
||||
</Button> |
||||
) |
||||
@ -1,53 +0,0 @@ |
||||
import type { MouseEvent } from 'react' |
||||
|
||||
import { |
||||
CloseButton, |
||||
Header, |
||||
HeaderActions, |
||||
} from 'features/PopupComponents' |
||||
import { InputGroup, NewInput } from 'features/Common' |
||||
|
||||
import { ButtonSolid } from '../../styled' |
||||
import { |
||||
Modal, |
||||
Body, |
||||
Title, |
||||
ButtonWrapper, |
||||
} from './styled' |
||||
|
||||
type Props = { |
||||
onHide: () => void, |
||||
} |
||||
|
||||
export const PasswordResetPopup = ({ onHide }: Props) => { |
||||
const close = (e: MouseEvent<HTMLButtonElement>) => { |
||||
e.stopPropagation() |
||||
onHide() |
||||
} |
||||
return ( |
||||
<Modal |
||||
isOpen |
||||
close={onHide} |
||||
withCloseButton={false} |
||||
> |
||||
<Header height={50}> |
||||
<Title> |
||||
Восстановление пароля |
||||
</Title> |
||||
<HeaderActions position='right'> |
||||
<CloseButton |
||||
onClick={close} |
||||
/> |
||||
</HeaderActions> |
||||
</Header> |
||||
<Body> |
||||
<InputGroup> |
||||
<NewInput type='email' placeholder='Введите email, указанный при регистрации' /> |
||||
</InputGroup> |
||||
<ButtonWrapper> |
||||
<ButtonSolid width='270px' onClick={close}>Отправить</ButtonSolid> |
||||
</ButtonWrapper> |
||||
</Body> |
||||
</Modal> |
||||
) |
||||
} |
||||
@ -1,57 +0,0 @@ |
||||
import styled, { css } from 'styled-components/macro' |
||||
|
||||
import { isMobileDevice } from 'config/userAgent' |
||||
|
||||
import { Modal as BaseModal } from 'features/Modal' |
||||
import { ModalWindow } from 'features/Modal/styled' |
||||
import { HeaderTitle } from 'features/PopupComponents' |
||||
|
||||
export const Modal = styled(BaseModal)` |
||||
background-color: rgba(0, 0, 0, 0.7); |
||||
|
||||
${ModalWindow} { |
||||
width: 577px; |
||||
padding: 15px 0; |
||||
background-color: #333333; |
||||
border-radius: 5px; |
||||
${isMobileDevice |
||||
? css` |
||||
width: 100%; |
||||
padding: 15px 30px 0; |
||||
` |
||||
: ''} |
||||
} |
||||
` |
||||
|
||||
export const Body = styled.div` |
||||
display: flex; |
||||
flex-direction: column; |
||||
align-items: center; |
||||
|
||||
margin-top: 24px; |
||||
padding: 0 40px; |
||||
${isMobileDevice |
||||
? css` |
||||
width: 100%; |
||||
padding: 0; |
||||
@media screen and (orientation: landscape){ |
||||
margin-top: 10px; |
||||
} |
||||
` |
||||
: ''} |
||||
` |
||||
|
||||
export const Title = styled(HeaderTitle)`` |
||||
|
||||
export const ButtonWrapper = styled.div` |
||||
padding: 25px 0; |
||||
${isMobileDevice |
||||
? css` |
||||
width: 100%; |
||||
padding: 12px 0 35px; |
||||
@media screen and (orientation: landscape){ |
||||
padding-top: 20px; |
||||
} |
||||
` |
||||
: ''} |
||||
` |
||||
@ -1,16 +0,0 @@ |
||||
import type { FormEvent } from 'react' |
||||
|
||||
import { useAuthStore } from 'features/AuthStore' |
||||
|
||||
export const useLoginForm = () => { |
||||
const { login } = useAuthStore() |
||||
|
||||
const handleSubmit = async (event: FormEvent<HTMLFormElement>) => { |
||||
event.preventDefault() |
||||
login() |
||||
} |
||||
|
||||
return { |
||||
handleSubmit, |
||||
} |
||||
} |
||||
@ -1,59 +0,0 @@ |
||||
import { PAGES } from 'config' |
||||
|
||||
import { T9n } from 'features/T9n' |
||||
import { Logo } from 'features/Logo' |
||||
|
||||
import { useLoginForm } from './hooks' |
||||
import { |
||||
BlockTitle, |
||||
CenterBlock, |
||||
ButtonsBlock, |
||||
Form, |
||||
RegisterButton, |
||||
ButtonSolid, |
||||
// Or,
|
||||
// BorderlessButton,
|
||||
} from './styled' |
||||
// import { AuthProviderButton } from './components/AuthProviderButton'
|
||||
// import { PasswordResetPopup } from './components/PasswordResetPopup'
|
||||
|
||||
const Login = () => { |
||||
const { |
||||
handleSubmit, |
||||
} = useLoginForm() |
||||
|
||||
return ( |
||||
<CenterBlock> |
||||
<Logo /> |
||||
<Form onSubmit={handleSubmit}> |
||||
<BlockTitle t='step_title_login' /> |
||||
|
||||
<ButtonsBlock> |
||||
<ButtonSolid type='submit'> |
||||
<T9n t='login' /> |
||||
</ButtonSolid> |
||||
<RegisterButton to={PAGES.register}> |
||||
<T9n t='register' /> |
||||
</RegisterButton> |
||||
</ButtonsBlock> |
||||
{/* TODO: раскомментить когда будет готово */} |
||||
{/* <Or>или</Or> |
||||
<AuthProviderButton provider='facebook' /> |
||||
<AuthProviderButton provider='google' /> |
||||
<BorderlessButton |
||||
type='button' |
||||
onClick={toggleResetPopup} |
||||
> |
||||
Забыли пароль? |
||||
</BorderlessButton> |
||||
{ |
||||
isResetPopupOpen && ( |
||||
<PasswordResetPopup onHide={toggleResetPopup} /> |
||||
) |
||||
} */} |
||||
</Form> |
||||
</CenterBlock> |
||||
) |
||||
} |
||||
|
||||
export default Login |
||||
@ -1,157 +0,0 @@ |
||||
import { Link } from 'react-router-dom' |
||||
|
||||
import styled, { css } from 'styled-components/macro' |
||||
|
||||
import { isMobileDevice } from 'config/userAgent' |
||||
|
||||
import { |
||||
outlineButtonStyles, |
||||
ButtonSolid as BaseButtonSolid, |
||||
ButtonOutline, |
||||
} from 'features/Common' |
||||
import { T9n } from 'features/T9n' |
||||
|
||||
export const CenterBlock = styled.div` |
||||
display: flex; |
||||
flex-direction: column; |
||||
align-items: center; |
||||
justify-content: flex-start; |
||||
margin-top: 6.133rem; |
||||
${isMobileDevice |
||||
? css` |
||||
margin-top: 110px; |
||||
@media screen and (orientation: landscape) { |
||||
width: 290px; |
||||
margin: auto; |
||||
} |
||||
` |
||||
: ''}; |
||||
` |
||||
|
||||
export const Form = styled.form<{ forRegPage?: boolean }>` |
||||
width: 23.255rem; |
||||
margin-top: 1.982rem; |
||||
margin-bottom: 6.604rem; |
||||
display: flex; |
||||
flex-direction: column; |
||||
align-items: center; |
||||
${isMobileDevice |
||||
? css` |
||||
width: 100%; |
||||
padding: 0 28px; |
||||
margin-top: 22px; |
||||
@media screen and (orientation: landscape){ |
||||
margin-bottom: 20px; |
||||
margin-top: 10px; |
||||
padding: 0; |
||||
} |
||||
` |
||||
: ''}; |
||||
` |
||||
|
||||
export const BlockTitle = styled(T9n)` |
||||
display: block; |
||||
font-style: normal; |
||||
font-weight: bold; |
||||
font-size: 1.14rem; |
||||
line-height: 1.14rem; |
||||
color: ${({ theme: { colors } }) => colors.text100}; |
||||
margin-bottom: 1.3rem; |
||||
transition: color 0.3s ease-in-out; |
||||
${isMobileDevice |
||||
? css` |
||||
font-size: 14.77px; |
||||
line-height: 14.77px;
|
||||
margin-bottom: 18.46px; |
||||
` |
||||
: ''}; |
||||
` |
||||
|
||||
type ButtonsBlockProps = { |
||||
marginTop?: number, |
||||
} |
||||
|
||||
export const ButtonsBlock = styled.div<ButtonsBlockProps>` |
||||
width: 100%; |
||||
display: flex; |
||||
flex-direction: column; |
||||
align-items: flex-start; |
||||
margin-top: ${({ marginTop = 0 }) => marginTop}px; |
||||
` |
||||
|
||||
type ButtonSolidProps = { |
||||
width?: string, |
||||
} |
||||
|
||||
export const ButtonSolid = styled(BaseButtonSolid)<ButtonSolidProps>` |
||||
width: ${({ width = '100%' }) => width}; |
||||
border-radius: 5px; |
||||
font-weight: normal; |
||||
${isMobileDevice |
||||
? css` |
||||
display: block; |
||||
width: 100%; |
||||
font-size: 12.31px; |
||||
@media screen and (orientation: landscape) { |
||||
max-width: 290px; |
||||
margin: auto; |
||||
} |
||||
` |
||||
: ''};
|
||||
` |
||||
|
||||
export const RegisterButton = styled(Link)` |
||||
${outlineButtonStyles} |
||||
width: 100%; |
||||
border-radius: 5px; |
||||
margin-top: 0.71rem; |
||||
display: flex; |
||||
align-items: center; |
||||
justify-content: center; |
||||
${isMobileDevice |
||||
? css` |
||||
min-height: 30px;
|
||||
margin-top: 9.23px; |
||||
font-size: 12.31px; |
||||
@media screen and (orientation: landscape) { |
||||
max-width: 290px; |
||||
margin: 9.23px auto 0; |
||||
} |
||||
` |
||||
: ''}; |
||||
` |
||||
|
||||
export const BorderlessButton = styled(ButtonOutline)` |
||||
border: none; |
||||
padding: 0; |
||||
width: auto; |
||||
height: 2.123rem; |
||||
font-weight: normal; |
||||
font-size: 0.755rem; |
||||
align-self: flex-start; |
||||
${isMobileDevice |
||||
? css` |
||||
font-size: 9.8px; |
||||
` |
||||
: ''}; |
||||
` |
||||
|
||||
export const Or = styled.span` |
||||
margin-top: 0.85rem; |
||||
margin-bottom: 0.65rem; |
||||
color: ${({ theme: { colors } }) => colors.text100}; |
||||
font-style: normal; |
||||
font-weight: normal; |
||||
font-size: 0.755rem; |
||||
line-height: 2.123rem; |
||||
${isMobileDevice |
||||
? css` |
||||
font-size: 14.77px; |
||||
padding: 20px 0;
|
||||
margin: 0; |
||||
@media screen and (orientation: landscape){ |
||||
padding: 10px 0; |
||||
} |
||||
` |
||||
: ''}; |
||||
` |
||||
@ -1,11 +0,0 @@ |
||||
import { useAuthForm } from 'hooks/useAuthForm' |
||||
|
||||
import { useAuthStore } from 'features/AuthStore' |
||||
|
||||
export const useRegistrationForm = () => { |
||||
const { register } = useAuthStore() |
||||
|
||||
return { |
||||
...useAuthForm({ onSubmit: register }), |
||||
} |
||||
} |
||||
@ -1,85 +0,0 @@ |
||||
import styled, { css } from 'styled-components/macro' |
||||
|
||||
import { useHistory } from 'react-router' |
||||
|
||||
import { isMobileDevice } from 'config/userAgent' |
||||
|
||||
import { T9n } from 'features/T9n' |
||||
import { |
||||
ButtonOutline as ButtonOutlineBase, |
||||
NewInput, |
||||
InputGroup, |
||||
PasswordInput, |
||||
} from 'features/Common' |
||||
import { Error } from 'features/Common/Input/styled' |
||||
import { |
||||
BlockTitle, |
||||
ButtonsBlock, |
||||
ButtonSolid, |
||||
Form, |
||||
} from 'features/Login/styled' |
||||
|
||||
import { useRegistrationForm } from './hooks' |
||||
|
||||
const ButtonOutline = styled(ButtonOutlineBase)` |
||||
width: 100%; |
||||
margin-top: 0.71rem; |
||||
border-radius: 5px; |
||||
${isMobileDevice |
||||
? css` |
||||
margin-top: 9.23px; |
||||
min-height: 30px; |
||||
font-size: 12.31px; |
||||
` |
||||
: ''}; |
||||
` |
||||
|
||||
export const RegistrationStep = () => { |
||||
const history = useHistory() |
||||
const { |
||||
email, |
||||
error, |
||||
handleSubmit, |
||||
onEmailBlur, |
||||
onEmailChange, |
||||
onPasswordBlur, |
||||
onPasswordChange, |
||||
password, |
||||
} = useRegistrationForm() |
||||
|
||||
return ( |
||||
<Form onSubmit={handleSubmit} forRegPage> |
||||
<BlockTitle t='step_title_registration' /> |
||||
<InputGroup> |
||||
<NewInput |
||||
type='email' |
||||
placeholderLexic='form_email' |
||||
value={email} |
||||
onChange={onEmailChange} |
||||
onBlur={onEmailBlur} |
||||
/> |
||||
<PasswordInput |
||||
placeholderLexic='form_password' |
||||
value={password} |
||||
onChange={onPasswordChange} |
||||
onBlur={onPasswordBlur} |
||||
/> |
||||
</InputGroup> |
||||
<ButtonsBlock> |
||||
<Error t={error} marginBottom={0.472} /> |
||||
<ButtonSolid |
||||
type='submit' |
||||
disabled={!email || !password || Boolean(error)} |
||||
> |
||||
<T9n t='sign_up' /> |
||||
</ButtonSolid> |
||||
<ButtonOutline |
||||
type='button' |
||||
onClick={history.goBack} |
||||
> |
||||
<T9n t='go_back' /> |
||||
</ButtonOutline> |
||||
</ButtonsBlock> |
||||
</Form> |
||||
) |
||||
} |
||||
@ -1,20 +0,0 @@ |
||||
import { PAGES } from 'config' |
||||
|
||||
import { T9n } from 'features/T9n' |
||||
import { BlockTitle } from 'features/Login/styled' |
||||
|
||||
import { |
||||
Container, |
||||
GreenTick, |
||||
ButtonLink, |
||||
} from './styled' |
||||
|
||||
export const RegistrationSuccessful = () => ( |
||||
<Container> |
||||
<BlockTitle t='registration_successful' /> |
||||
<GreenTick /> |
||||
<ButtonLink to={PAGES.login}> |
||||
<T9n t='login' /> |
||||
</ButtonLink> |
||||
</Container> |
||||
) |
||||
@ -1,71 +0,0 @@ |
||||
import { Link } from 'react-router-dom' |
||||
import styled, { css } from 'styled-components/macro' |
||||
|
||||
import { devices } from 'config/devices' |
||||
import { isMobileDevice } from 'config/userAgent' |
||||
|
||||
import { solidButtonStyles } from 'features/Common' |
||||
import { BlockTitle } from 'features/Login/styled' |
||||
|
||||
export const Container = styled.div` |
||||
display: flex; |
||||
flex-direction: column; |
||||
align-items: center; |
||||
margin-top: 80px; |
||||
${isMobileDevice |
||||
? css` |
||||
@media screen and (orientation: landscape){ |
||||
margin-top: 20px; |
||||
}
|
||||
` |
||||
: ''}; |
||||
|
||||
@media ${devices.tablet} { |
||||
|
||||
${BlockTitle} { |
||||
text-align: center; |
||||
} |
||||
} |
||||
|
||||
@media ${devices.mobile} { |
||||
margin-top: 20px; |
||||
} |
||||
` |
||||
|
||||
export const GreenTick = styled.div` |
||||
width: 288px; |
||||
height: 288px; |
||||
margin: 60px 0 156px 0; |
||||
background-image: url(/images/greenTick.svg); |
||||
background-position-y: -98px; |
||||
background-position-x: center; |
||||
|
||||
${isMobileDevice |
||||
? css` |
||||
width: 200px; |
||||
height: 200px; |
||||
background-repeat: no-repeat; |
||||
background-position: center; |
||||
background-size: contain; |
||||
margin: 0 auto 15%; |
||||
@media screen and (orientation: landscape){ |
||||
margin: 0; |
||||
} |
||||
` |
||||
: ''}; |
||||
` |
||||
|
||||
export const ButtonLink = styled(Link)` |
||||
${solidButtonStyles} |
||||
display: flex; |
||||
justify-content: center; |
||||
align-items: center; |
||||
${isMobileDevice |
||||
? css` |
||||
border-radius: 5px; |
||||
font-size: 12.31px; |
||||
font-weight: normal; |
||||
` |
||||
: ''}; |
||||
|
||||
` |
||||
@ -1,23 +0,0 @@ |
||||
import { Route } from 'react-router' |
||||
|
||||
import { PAGES } from 'config' |
||||
|
||||
import { Logo } from 'features/Logo' |
||||
import { CenterBlock } from 'features/Login/styled' |
||||
|
||||
import { RegistrationStep } from './components/RegistrationStep' |
||||
import { RegistrationSuccessful } from './components/RegistrationSuccessful' |
||||
|
||||
const Register = () => ( |
||||
<CenterBlock> |
||||
<Logo /> |
||||
<Route exact path={`${PAGES.register}`}> |
||||
<RegistrationStep /> |
||||
</Route> |
||||
<Route exact path={`${PAGES.register}/successful`}> |
||||
<RegistrationSuccessful /> |
||||
</Route> |
||||
</CenterBlock> |
||||
) |
||||
|
||||
export default Register |
||||
@ -1,22 +0,0 @@ |
||||
import { isValidEmail } from '..' |
||||
|
||||
it('invalid emails', () => { |
||||
expect(isValidEmail('a')).toBeFalsy() |
||||
expect(isValidEmail('1')).toBeFalsy() |
||||
expect(isValidEmail('a@')).toBeFalsy() |
||||
expect(isValidEmail('a@m')).toBeFalsy() |
||||
expect(isValidEmail('a@m.')).toBeFalsy() |
||||
expect(isValidEmail('a@mail')).toBeFalsy() |
||||
expect(isValidEmail('a@mail.')).toBeFalsy() |
||||
expect(isValidEmail('abcd.mail.com')).toBeFalsy() |
||||
}) |
||||
|
||||
it('valid emails', () => { |
||||
expect(isValidEmail('a@mail.com')).toBeTruthy() |
||||
expect(isValidEmail('A@MAIL.COM')).toBeTruthy() |
||||
expect(isValidEmail('123@mail.com')).toBeTruthy() |
||||
expect(isValidEmail('A123@mail.com')).toBeTruthy() |
||||
expect(isValidEmail('a.b@mail.com')).toBeTruthy() |
||||
expect(isValidEmail('a-b-c@mail.com')).toBeTruthy() |
||||
expect(isValidEmail('a-b@a-b.com')).toBeTruthy() |
||||
}) |
||||
@ -1,9 +0,0 @@ |
||||
import size from 'lodash/size' |
||||
|
||||
export const emailRegex = '[a-z0-9!#$%&/\'*+/=?^_`{|}~-]+(?:\\.[a-z0-9!#$%&/\'*+/=?^_`{|}~-]+)*@(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\\.)+[a-z0-9](?:[a-z0-9-]*[a-z0-9])?' |
||||
|
||||
const emailRegExp = new RegExp(emailRegex) |
||||
|
||||
export const isValidEmail = (email: string) => ( |
||||
emailRegExp.test(email.toLowerCase()) && size(email) <= 100 |
||||
) |
||||
@ -1,62 +0,0 @@ |
||||
import type { FocusEvent, FormEvent } from 'react' |
||||
import { useState } from 'react' |
||||
|
||||
import { isValidEmail } from 'helpers/isValidEmail' |
||||
import { isValidPassword } from 'helpers/isValidPassword' |
||||
|
||||
export type Values = { |
||||
email: string, |
||||
password: string, |
||||
} |
||||
|
||||
type Args = { |
||||
onSubmit: (values: Values) => Promise<void>, |
||||
} |
||||
|
||||
export const useAuthForm = ({ onSubmit }: Args) => { |
||||
const [email, setEmail] = useState('') |
||||
const [password, setPassword] = useState('') |
||||
const [error, setError] = useState('') |
||||
|
||||
const onEmailChange = ({ target: { value } }: FocusEvent<HTMLInputElement>) => { |
||||
setError('') |
||||
setEmail(value) |
||||
} |
||||
|
||||
const onPasswordChange = ({ target: { value } }: FocusEvent<HTMLInputElement>) => { |
||||
setError('') |
||||
setPassword(value) |
||||
} |
||||
|
||||
const onEmailBlur = ({ target: { value } }: FocusEvent<HTMLInputElement>) => { |
||||
if (!value) { |
||||
setError('error_empty_email') |
||||
} else if (!isValidEmail(value)) { |
||||
setError('error_invalid_email_format') |
||||
} |
||||
} |
||||
|
||||
const onPasswordBlur = ({ target: { value } }: FocusEvent<HTMLInputElement>) => { |
||||
if (!value) { |
||||
setError('error_empty_password') |
||||
} else if (!isValidPassword(value)) { |
||||
setError('error_simple_password') |
||||
} |
||||
} |
||||
|
||||
const handleSubmit = async (event: FormEvent<HTMLFormElement>) => { |
||||
event.preventDefault() |
||||
onSubmit({ email, password }).catch(setError) |
||||
} |
||||
|
||||
return { |
||||
email, |
||||
error, |
||||
handleSubmit, |
||||
onEmailBlur, |
||||
onEmailChange, |
||||
onPasswordBlur, |
||||
onPasswordChange, |
||||
password, |
||||
} |
||||
} |
||||
@ -1,54 +0,0 @@ |
||||
import { DATA_URL, PROCEDURES } from 'config' |
||||
import { callApiBase } from 'helpers' |
||||
|
||||
const proc = PROCEDURES.create_user |
||||
|
||||
const statusCodes = { |
||||
EMAIL_IN_USE: 2, |
||||
SUCCESS: 1, |
||||
} |
||||
|
||||
const errorMessages = { |
||||
[statusCodes.EMAIL_IN_USE]: 'error_email_already_in_use', |
||||
} |
||||
|
||||
type Response = { |
||||
_p_error: string | null, |
||||
_p_status: 1 | 2, |
||||
} |
||||
|
||||
type Args = { |
||||
email: string, |
||||
password: string, |
||||
} |
||||
|
||||
export const register = async ({ |
||||
email, |
||||
password, |
||||
}: Args) => { |
||||
const config = { |
||||
body: { |
||||
params: { |
||||
_p_email: email, |
||||
_p_password: password, |
||||
}, |
||||
proc, |
||||
}, |
||||
} |
||||
|
||||
try { |
||||
const response = await callApiBase({ |
||||
config, |
||||
url: DATA_URL, |
||||
}) |
||||
|
||||
const { _p_status }: Response = await response.json() |
||||
|
||||
if (_p_status === statusCodes.SUCCESS) { |
||||
return Promise.resolve(response) |
||||
} |
||||
return Promise.reject(errorMessages[_p_status]) |
||||
} catch { |
||||
return Promise.reject() |
||||
} |
||||
} |
||||
Loading…
Reference in new issue