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 token
keep-around/af30b88d367751c9e05a735e4a0467a96238ef47
Mirlan 4 years ago committed by GitHub
parent 6945f318a4
commit 92d4d6be4c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 0
      src/config/languages.tsx
  2. 20
      src/config/lexics/public.tsx
  3. 3
      src/config/procedures.tsx
  4. 16
      src/features/App/RedirectCallback.tsx
  5. 76
      src/features/App/UnauthenticatedApp.tsx
  6. 3
      src/features/App/index.tsx
  7. 3
      src/features/AuthStore/helpers.tsx
  8. 65
      src/features/AuthStore/hooks/useAuth.tsx
  9. 18
      src/features/AuthStore/hooks/useRegister.tsx
  10. 1
      src/features/HeaderFilters/components/DateFilter/index.tsx
  11. 66
      src/features/LanguageSelect/index.tsx
  12. 145
      src/features/LanguageSelect/styled.tsx
  13. 2
      src/features/LexicsStore/helpers/isSupportedLang/index.tsx
  14. 2
      src/features/LexicsStore/hooks/useLang.tsx
  15. 74
      src/features/Login/components/AuthProviderButton/index.tsx
  16. 53
      src/features/Login/components/PasswordResetPopup/index.tsx
  17. 57
      src/features/Login/components/PasswordResetPopup/styled.tsx
  18. 16
      src/features/Login/hooks.tsx
  19. 59
      src/features/Login/index.tsx
  20. 157
      src/features/Login/styled.tsx
  21. 11
      src/features/Register/components/RegistrationStep/hooks/index.tsx
  22. 85
      src/features/Register/components/RegistrationStep/index.tsx
  23. 20
      src/features/Register/components/RegistrationSuccessful/index.tsx
  24. 71
      src/features/Register/components/RegistrationSuccessful/styled.tsx
  25. 23
      src/features/Register/index.tsx
  26. 2
      src/features/UserAccount/components/PagePersonalInfo/hooks/index.tsx
  27. 4
      src/features/UserAccount/components/PersonalInfoForm/config.tsx
  28. 2
      src/features/UserAccount/components/PersonalInfoForm/hooks/useUserInfo.tsx
  29. 22
      src/helpers/isValidEmail/__tests__/index.tsx
  30. 9
      src/helpers/isValidEmail/index.tsx
  31. 62
      src/hooks/useAuthForm.tsx
  32. 1
      src/requests/index.tsx
  33. 54
      src/requests/register.tsx

@ -1,12 +1,4 @@
export const publicLexics = {
error_account_blocked: 12909,
error_email_already_in_use: 11156,
error_empty_email: 2498,
error_empty_password: 2499,
error_fill_out_required_fields: 12911,
error_fill_out_this_field: 12933,
error_invalid_email_format: 12908,
error_invalid_email_or_password: 2502,
error_invalid_phone_format: 12946,
error_select_country_first: 12910,
error_simple_password: 12940,
@ -25,17 +17,5 @@ export const publicLexics = {
form_postal_code: 12913,
form_region: 12932,
go_back: 1907,
login: 13404,
next: 10875,
register: 13328,
registration_successful: 12945,
select_language: 1005,
sign_up: 1305,
step_title_card: 12917,
step_title_login: 13404,
step_title_registration: 1306,
step_title_subscription: 12919,
subscription_done: 2668,
subscription_extra: 6036,
subscription_for: 12920,
}

@ -1,7 +1,5 @@
export const PROCEDURES = {
auth_user: 'auth_user',
bind_ott_subscription: 'bind_ott_subscription',
create_user: 'create_user',
get_cities: 'get_cities',
get_match_info: 'get_match_info',
get_match_subscriptions: 'get_match_subscriptions',
@ -19,7 +17,6 @@ export const PROCEDURES = {
get_user_info: 'get_user_info',
get_user_match_second: 'get_user_match_second',
get_user_subscriptions: 'get_user_subscriptions',
logout_user: 'logout_user',
lst_c_country: 'lst_c_country',
ott_match_popup: 'ott_match_popup',
ott_match_popup_actions: 'ott_match_popup_actions',

@ -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>
)
}

@ -10,7 +10,6 @@ import { GlobalStyles } from 'features/GlobalStyles'
import { Theme } from 'features/Theme'
import { AuthenticatedApp } from './AuthenticatedApp'
import { UnauthenticatedApp } from './UnauthenticatedApp'
const Main = () => {
const { loadingUser, user } = useAuthStore()
@ -19,8 +18,6 @@ const Main = () => {
// access_token токен истек и запрашивается новый
if (loadingUser || user?.expired) return null
if (!user) return <UnauthenticatedApp />
// имеется действующий токен
return <AuthenticatedApp />
}

@ -8,12 +8,11 @@ export const getClientSettings = (): UserManagerSettings => ({
filterProtocolClaims: false,
loadUserInfo: false,
metadataUrl: 'https://auth.instat.tv/.well-known/openid-configuration',
post_logout_redirect_uri: `${window.origin}/login`,
redirect_uri: `${window.origin}/redirect`,
response_mode: 'query',
response_type: 'id_token token',
scope: 'openid',
silent_redirect_uri: `${window.origin}/silent-refresh.html`,
silentRequestTimeout: 5000,
silentRequestTimeout: 2000,
userStore: new WebStorageStateStore({ store: window.localStorage }),
})

@ -4,19 +4,24 @@ import {
useMemo,
useEffect,
} from 'react'
import { useHistory } from 'react-router'
import type { User } from 'oidc-client'
import { UserManager } from 'oidc-client'
import { PAGES } from 'config'
import { writeToken, removeToken } from 'helpers/token'
import { setCookie, removeCookie } from 'helpers/cookie'
import { useToggle } from 'hooks'
import { queryParamStorage } from 'features/QueryParamsStorage'
import { getClientSettings } from '../helpers'
import { useRegister } from './useRegister'
export const useAuth = () => {
const history = useHistory()
const {
close: markUserLoaded,
isOpen: loadingUser,
@ -28,16 +33,16 @@ export const useAuth = () => {
userManager.signinRedirect()
), [userManager])
const logout = useCallback(async () => {
userManager.signoutRedirect()
const logout = useCallback(() => {
userManager.clearStaleState()
userManager.createSigninRequest().then(({ url }) => {
userManager.signoutRedirect({ post_logout_redirect_uri: url })
})
removeToken()
removeCookie('access_token')
}, [userManager])
const register = useRegister()
const storeUser = useCallback(async (loadedUser: User) => {
const storeUser = useCallback((loadedUser: User) => {
setUser(loadedUser)
writeToken(loadedUser.access_token)
setCookie({
@ -49,16 +54,44 @@ export const useAuth = () => {
const checkUser = useCallback(async () => {
const loadedUser = await userManager.getUser()
if (loadedUser) {
storeUser(loadedUser)
}
if (!loadedUser) return Promise.reject()
storeUser(loadedUser)
markUserLoaded()
return loadedUser
}, [
userManager,
storeUser,
markUserLoaded,
])
const signinRedirectCallback = useCallback(async () => {
const loadedUser = await userManager.signinRedirectCallback()
storeUser(loadedUser)
queryParamStorage.clear()
history.replace(PAGES.home)
markUserLoaded()
}, [userManager, storeUser, markUserLoaded])
}, [
userManager,
history,
storeUser,
markUserLoaded,
])
useEffect(() => {
// при первой загрузке считываем пользователя из localstorage
checkUser()
}, [checkUser])
const isRedirectedBackFromAuthProvider = history.location.pathname === '/redirect'
if (isRedirectedBackFromAuthProvider) {
signinRedirectCallback()
} else {
// обычная загрузка страницы, проверяем пользователя в localstorage
checkUser().catch(login)
}
}, [
checkUser,
signinRedirectCallback,
login,
history,
])
useEffect(() => {
// попытаемся обновить токен используя refresh_token
@ -84,18 +117,12 @@ export const useAuth = () => {
const auth = useMemo(() => ({
loadingUser,
login,
logout,
register,
signinRedirectCallback: () => userManager.signinRedirectCallback(),
user,
}), [
login,
logout,
register,
user,
loadingUser,
userManager,
])
return auth

@ -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)
)
}

@ -54,6 +54,7 @@ export const DateFilter = () => {
{
map(week, (day) => (
<WeekDay
key={day.name}
selected={day.date.getDate() === selectedDate.getDate()}
onClick={() => onWeekDayClick(day.date)}
>

@ -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,7 +1,7 @@
import map from 'lodash/map'
import includes from 'lodash/includes'
import { langsList } from 'features/LanguageSelect/config'
import { langsList } from 'config/languages'
export const isSupportedLang = (lang: string) => {
const supportedLangs = map(langsList, 'locale')

@ -2,7 +2,7 @@ import { useCallback } from 'react'
import { useLocalStore } from 'hooks'
import type { Languages } from 'features/LanguageSelect/config'
import type { Languages } from 'config/languages'
import { isSupportedLang } from '../helpers/isSupportedLang'

@ -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

@ -12,6 +12,7 @@ import isNumber from 'lodash/isNumber'
import isString from 'lodash/isString'
import isObject from 'lodash/isObject'
import type { Languages } from 'config/languages'
import { formIds } from 'config/form'
import type { UserInfo, SaveUserInfo } from 'requests'
@ -22,7 +23,6 @@ import {
} from 'requests'
import type { FormState } from 'features/FormStore/hooks/useFormState'
import type { Languages } from 'features/LanguageSelect/config'
import { useLexicsStore } from 'features/LexicsStore'
export type SaveWithLang = SaveUserInfo & {

@ -1,7 +1,7 @@
import map from 'lodash/map'
import type { Languages } from 'features/LanguageSelect/config'
import { langsList } from 'features/LanguageSelect/config'
import type { Languages } from 'config/languages'
import { langsList } from 'config/languages'
export type Lang = {
id: Languages,

@ -3,9 +3,9 @@ import { useCallback, useMemo } from 'react'
import trim from 'lodash/trim'
import find from 'lodash/find'
import type { Languages } from 'config/languages'
import { formIds } from 'config/form'
import type { Languages } from 'features/LanguageSelect/config'
import { useForm } from 'features/FormStore'
import { useUserInfoForm } from './useUserInfoForm'

@ -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,4 +1,3 @@
export * from './register'
export * from './getCountries'
export * from './getCountryCities'
export * from './getLexics'

@ -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…
Cancel
Save