diff --git a/src/config/pages.tsx b/src/config/pages.tsx index 570ecc08..edcfcdfa 100644 --- a/src/config/pages.tsx +++ b/src/config/pages.tsx @@ -1,4 +1,5 @@ export const PAGES = { + home: '/home', login: '/login', register: '/register', } diff --git a/src/config/procedures.tsx b/src/config/procedures.tsx index 257173e6..38e2a266 100644 --- a/src/config/procedures.tsx +++ b/src/config/procedures.tsx @@ -1,4 +1,5 @@ export const PROCEDURES = { + auth_user: 'auth_user', create_user: 'create_user', get_cities: 'get_cities', lst_c_country: 'lst_c_country', diff --git a/src/features/App/AuthenticatedApp.tsx b/src/features/App/AuthenticatedApp.tsx new file mode 100644 index 00000000..b70499f4 --- /dev/null +++ b/src/features/App/AuthenticatedApp.tsx @@ -0,0 +1,20 @@ +import React from 'react' +import { + Route, + Redirect, + Switch, +} from 'react-router-dom' + +import { PAGES } from 'config' + +import { HomePage } from 'features/HomePage' + +export const AuthenticatedApp = () => ( + + + + + + + +) diff --git a/src/features/App/UnauthenticatedApp.tsx b/src/features/App/UnauthenticatedApp.tsx new file mode 100644 index 00000000..a4e7a5a8 --- /dev/null +++ b/src/features/App/UnauthenticatedApp.tsx @@ -0,0 +1,25 @@ +import React from 'react' +import { + Route, + Redirect, + Switch, +} from 'react-router-dom' + +import { PAGES } from 'config' + +import { Login } from 'features/Login' +import { Register } from 'features/Register' + +export const UnauthenticatedApp = () => ( + + + + + + + + + + + +) diff --git a/src/features/App/index.tsx b/src/features/App/index.tsx index b4eb6673..f4dc79c5 100644 --- a/src/features/App/index.tsx +++ b/src/features/App/index.tsx @@ -1,36 +1,38 @@ import React from 'react' -import { - Router, - Route, - Redirect, - Switch, -} from 'react-router-dom' +import { Router } from 'react-router-dom' import { createBrowserHistory } from 'history' -import { PAGES } from 'config' +import { GlobalStores } from 'features/GlobalStores' +import { useAuthStore } from 'features/AuthStore' +import { Background } from 'features/Background' import { GlobalStyles } from 'features/GlobalStyles' import { Theme } from 'features/Theme' -import { Login } from 'features/Login' -import { Register } from 'features/Register' + +import { AuthenticatedApp } from './AuthenticatedApp' +import { UnauthenticatedApp } from './UnauthenticatedApp' const history = createBrowserHistory() +const Main = () => { + const { token } = useAuthStore() + return ( + + { + token + ? + : + } + + ) +} + export const App = () => ( - - - - - - - - - - - - + +
+ ) diff --git a/src/features/AuthStore/hooks/useLogin.tsx b/src/features/AuthStore/hooks/useLogin.tsx new file mode 100644 index 00000000..28ca33b8 --- /dev/null +++ b/src/features/AuthStore/hooks/useLogin.tsx @@ -0,0 +1,30 @@ +import { useHistory } from 'react-router-dom' + +import { PAGES } from 'config' +import { login } from 'requests' +import { writeToken } from 'helpers' + +type LoginArgs = Parameters[0] + +type Args = { + setToken: (token: string | null) => void, +} + +export const useLogin = ({ setToken }: Args) => { + const history = useHistory() + + const onSuccess = (token: string) => { + writeToken(token) + setToken(token) + history.replace(PAGES.home) + } + + const onError = (message: string) => { + // eslint-disable-next-line no-alert + window.alert(message) + } + + return async ({ email, password }: LoginArgs) => ( + login({ email, password }).then(onSuccess, onError) + ) +} diff --git a/src/features/AuthStore/hooks/useLogout.tsx b/src/features/AuthStore/hooks/useLogout.tsx new file mode 100644 index 00000000..1ff9b376 --- /dev/null +++ b/src/features/AuthStore/hooks/useLogout.tsx @@ -0,0 +1,26 @@ +import { useHistory } from 'react-router-dom' + +import { PAGES } from 'config' +import { removeToken } from 'helpers' +// import { logout } from 'requests' + +// временная заглушка запроса +const logout = () => Promise.resolve() + +type Args = { + setToken: (token: string | null) => void, +} + +export const useLogout = ({ setToken }: Args) => { + const history = useHistory() + + const onSuccess = () => { + removeToken() + setToken(null) + history.replace(PAGES.login) + } + + return async () => ( + logout().then(onSuccess) + ) +} diff --git a/src/features/AuthStore/hooks/useRegister.tsx b/src/features/AuthStore/hooks/useRegister.tsx new file mode 100644 index 00000000..732d83d3 --- /dev/null +++ b/src/features/AuthStore/hooks/useRegister.tsx @@ -0,0 +1,23 @@ +import { useHistory } from 'react-router-dom' + +import { PAGES } from 'config' +import { register } from 'requests' + +type Args = Parameters[0] + +export const useRegister = () => { + const history = useHistory() + + const goToLoginPage = () => { + history.replace(PAGES.login) + } + + const showError = (message: string) => { + // eslint-disable-next-line no-alert + window.alert(message) + } + + return async (args: Args) => ( + register(args).then(goToLoginPage, showError) + ) +} diff --git a/src/features/AuthStore/index.tsx b/src/features/AuthStore/index.tsx new file mode 100644 index 00000000..01b76d01 --- /dev/null +++ b/src/features/AuthStore/index.tsx @@ -0,0 +1,43 @@ +import type { ReactNode } from 'react' +import React, { + createContext, + useContext, + useState, +} from 'react' + +import { readToken } from 'helpers' + +import { useLogin } from './hooks/useLogin' +import { useLogout } from './hooks/useLogout' +import { useRegister } from './hooks/useRegister' + +type Auth = { + login: ReturnType, + logout: ReturnType, + register: ReturnType, + token: string | null, +} + +const AuthContext = createContext({} as Auth) + +type Props = { + children: ReactNode, +} + +export const AuthStore = ({ children }: Props) => { + const [token, setToken] = useState(readToken()) + const login = useLogin({ setToken }) + const logout = useLogout({ setToken }) + const register = useRegister() + + const auth = { + login, + logout, + register, + token, + } + + return {children} +} + +export const useAuthStore = () => useContext(AuthContext) diff --git a/src/features/GlobalStores/index.tsx b/src/features/GlobalStores/index.tsx new file mode 100644 index 00000000..5690cd0f --- /dev/null +++ b/src/features/GlobalStores/index.tsx @@ -0,0 +1,14 @@ +import React, { ReactNode } from 'react' + +import { AuthStore } from 'features/AuthStore' + +type Props = { + children: ReactNode, +} + +// аутентификация, лексики и другие глобальные сторы +export const GlobalStores = ({ children }: Props) => ( + + {children} + +) diff --git a/src/features/HomePage/index.tsx b/src/features/HomePage/index.tsx new file mode 100644 index 00000000..d046b534 --- /dev/null +++ b/src/features/HomePage/index.tsx @@ -0,0 +1,33 @@ +import React from 'react' + +import styled from 'styled-components/macro' + +import { Logo } from 'features/Logo' +import { CenterBlock } from 'features/Login/styled' +import { useAuthStore } from 'features/AuthStore' + +// временные компоненты +const TempContainer = styled.div` + margin-top: 50px; + color: white; + font-size: 20px; + display: flex; + flex-direction: column; +` + +const TempPageTitle = styled.span` + margin: 20px 0; +` + +export const HomePage = () => { + const { logout } = useAuthStore() + return ( + + + + HOME PAGE + + + + ) +} diff --git a/src/features/Login/hooks.tsx b/src/features/Login/hooks.tsx new file mode 100644 index 00000000..d98139c9 --- /dev/null +++ b/src/features/Login/hooks.tsx @@ -0,0 +1,26 @@ +import type { FormEvent } from 'react' + +import trim from 'lodash/trim' + +import { useAuthStore } from 'features/AuthStore' +import { formIds } from 'features/Register/components/RegistrationStep/config' + +const readFormValue = (event: FormEvent) => ( + (fieldName: string) => trim(event.currentTarget[fieldName]?.value) +) + +export const useForm = () => { + const { login } = useAuthStore() + + const handleSubmit = async (event: FormEvent) => { + event.preventDefault() + + const readFieldValue = readFormValue(event) + const email = readFieldValue(formIds.email) + const password = readFieldValue(formIds.password) + + login({ email, password }) + } + + return { handleSubmit } +} diff --git a/src/features/Login/index.tsx b/src/features/Login/index.tsx index 972d5af5..3f5ebe5e 100644 --- a/src/features/Login/index.tsx +++ b/src/features/Login/index.tsx @@ -2,13 +2,11 @@ import React from 'react' import { PAGES } from 'config' -import { Background } from 'features/Background' import { Logo } from 'features/Logo' -import { - Input, - ButtonSolid, -} from 'features/Common' +import { Input, ButtonSolid } from 'features/Common' +import { formIds } from 'features/Register/components/RegistrationStep/config' +import { useForm } from './hooks' import { BlockTitle, CenterBlock, @@ -19,35 +17,37 @@ import { const labelWidth = 60 -export const Login = () => ( - +export const Login = () => { + const { handleSubmit } = useForm() + + return ( -
+ Авторизация - Войти + Войти Зарегистрироваться
-
-) + ) +} diff --git a/src/features/Register/components/RegistrationStep/hooks/useForm.tsx b/src/features/Register/components/RegistrationStep/hooks/useForm.tsx index 94f64dcf..2363ff3a 100644 --- a/src/features/Register/components/RegistrationStep/hooks/useForm.tsx +++ b/src/features/Register/components/RegistrationStep/hooks/useForm.tsx @@ -1,12 +1,9 @@ import type { FormEvent } from 'react' import { useEffect } from 'react' -import { useHistory } from 'react-router-dom' import trim from 'lodash/trim' -import { PAGES } from 'config' -import { register } from 'requests' - +import { useAuthStore } from 'features/AuthStore' import { isValidEmail } from 'features/Register/helpers/isValidEmail' import { formIds } from '../config' @@ -18,7 +15,7 @@ const readFormValue = (event: FormEvent) => ( ) export const useForm = () => { - const history = useHistory() + const { register } = useAuthStore() const { countries, onCountrySelect, @@ -44,15 +41,6 @@ export const useForm = () => { resetCities, ]) - const goToLoginPage = () => { - history.replace(PAGES.login) - } - - const showError = (message: string) => { - // eslint-disable-next-line no-alert - window.alert(message) - } - const getCityParams = () => { if (selectedCity) return { cityId: selectedCity.id } return { city: cityQuery } @@ -86,7 +74,7 @@ export const useForm = () => { phone, postalCode, region, - }).then(goToLoginPage, showError) + }) } } diff --git a/src/features/Register/index.tsx b/src/features/Register/index.tsx index 02a5ba4d..a595e1a1 100644 --- a/src/features/Register/index.tsx +++ b/src/features/Register/index.tsx @@ -3,7 +3,6 @@ import { Route } from 'react-router' import { PAGES } from 'config' -import { Background } from 'features/Background' import { Logo } from 'features/Logo' import { CenterBlock } from 'features/Login/styled' @@ -12,18 +11,16 @@ import { CardStep } from './components/CardStep' import { SubscriptionStep } from './components/SubscriptionsStep' export const Register = () => ( - - - - - - - - - - - - - - + + + + + + + + + + + + ) diff --git a/src/helpers/callApi/getRequestConfig.tsx b/src/helpers/callApi/getRequestConfig.tsx index e9eb7f3f..14ebafab 100644 --- a/src/helpers/callApi/getRequestConfig.tsx +++ b/src/helpers/callApi/getRequestConfig.tsx @@ -1,6 +1,6 @@ import { AUTH_KEYS } from 'config' +import { readToken } from 'helpers' -import { loadIdToken } from './loadIdToken' import { TRequestConfig } from './types' export const getRequestConfig = ( @@ -19,7 +19,7 @@ export const getRequestConfig = ( requestConfig.headers.set('Content-Type', 'application/json') } - const token = loadIdToken() + const token = readToken() if (token) { requestConfig.headers.set(AUTH_KEYS.headerToken, token) diff --git a/src/helpers/callApi/loadIdToken.tsx b/src/helpers/callApi/loadIdToken.tsx deleted file mode 100644 index c1f70feb..00000000 --- a/src/helpers/callApi/loadIdToken.tsx +++ /dev/null @@ -1 +0,0 @@ -export const loadIdToken = () => localStorage.getItem('id_token') diff --git a/src/helpers/index.tsx b/src/helpers/index.tsx index 1ba6128d..1c31f046 100644 --- a/src/helpers/index.tsx +++ b/src/helpers/index.tsx @@ -1,2 +1,3 @@ export * from './callApi' export * from './callApi/getResponseData' +export * from './token' diff --git a/src/helpers/token/index.tsx b/src/helpers/token/index.tsx new file mode 100644 index 00000000..06eb165b --- /dev/null +++ b/src/helpers/token/index.tsx @@ -0,0 +1,13 @@ +const TOKEN_KEY = 'token' + +export const readToken = () => ( + localStorage.getItem(TOKEN_KEY) +) + +export const writeToken = (token: string) => ( + localStorage.setItem(TOKEN_KEY, token) +) + +export const removeToken = () => ( + localStorage.removeItem(TOKEN_KEY) +) diff --git a/src/requests/login.tsx b/src/requests/login.tsx index 627dbbf4..dd83c27f 100644 --- a/src/requests/login.tsx +++ b/src/requests/login.tsx @@ -1 +1,51 @@ -export const login = () => {} +import { DATA_URL, PROCEDURES } from 'config' +import { callApi, writeToken } from 'helpers' + +const proc = PROCEDURES.auth_user + +const statusCodes = { + ACCOUNT_EXPIRED: 5, + EMAIL_NOT_FOUND: 2, + INVALID_CREDENTIALS: 3, + SUCCESS: 1, + USER_REMOVED_OR_BLOCKED: 4, +} as const + +type StatusCodes = typeof statusCodes[keyof typeof statusCodes] + +type Response = { + _p_status: StatusCodes, + _p_token: string, +} + +type Args = { + email: string, + password: string, +} + +export const login = async ({ + email, + password, +}: Args) => { + const config = { + body: { + params: { + _p_email: email, + _p_password: password, + }, + proc, + }, + } + + const response: Response = await callApi({ + config, + url: DATA_URL, + }) + + if (response._p_status === statusCodes.SUCCESS) { + writeToken(response._p_token) + return Promise.resolve(response._p_token) + } + + return Promise.reject(response._p_status) +}