diff --git a/src/components/login/LoginForm/index.tsx b/src/components/login/LoginForm/index.tsx deleted file mode 100644 index 152af805..00000000 --- a/src/components/login/LoginForm/index.tsx +++ /dev/null @@ -1,103 +0,0 @@ -import * as React from 'react'; -import { TextInput } from "~/components/input/TextInput"; -import { Button } from "~/components/input/Button"; -import { connect } from 'react-redux'; -import { bindActionCreators } from "redux"; -import { userSendLoginRequest, userSetLoginError } from "~/redux/user/actions"; -import { IUserFormStateLogin, IUserState } from "~/redux/user/reducer"; -import { Info } from "~/components/input/Info"; - -import * as login from '~/containers/login/LoginLayout/styles.scss'; -import * as style from './style.scss'; - -interface ILoginFormProps { - error: IUserFormStateLogin['error'], - - userSendLoginRequest: typeof userSendLoginRequest, - userSetLoginError: typeof userSetLoginError, -} - -interface ILoginFormState { - username: string, - password: string, -} - -class Component extends React.PureComponent { - state = { - username: 'user', - password: 'password', - }; - - sendRequest = () => { - console.log('send?'); - this.props.userSendLoginRequest(this.state); - }; - - changeField = (field: T) => ({ target: { value }}: React.ChangeEvent) => { - if (this.props.error) this.props.userSetLoginError({ error: null }); - this.setState({ [field]: value } as Pick); - }; - - render() { - const { error } = this.props; - const { username, password } = this.state; - - return ( -
-
-
- -
-
-
- РЕШИТЕЛЬНО
ВОЙТИ -
- -
- -
- - -
- - - -
- - - - { - error && - -
- - {error} - - - } -
-
-
-
- ) - } -} - -const mapStateToProps = ({ user: { form_state: { login }}}: { user: IUserState }) => ({ ...login }); -const mapDispatchToProps = dispatch => bindActionCreators({ - userSendLoginRequest, - userSetLoginError, -}, dispatch); - -export const LoginForm = connect(mapStateToProps, mapDispatchToProps)(Component); diff --git a/src/components/login/LoginForm/style.scss b/src/components/login/LoginForm/style.scss deleted file mode 100644 index 97a1c82d..00000000 --- a/src/components/login/LoginForm/style.scss +++ /dev/null @@ -1,44 +0,0 @@ -.container { - display: grid; - flex: 1; - - grid-template-columns: repeat(4, 1fr); - grid-template-rows: 1fr; - grid-row-gap: $grid_line; - grid-column-gap: $grid_line; -} - -.area_left { - grid-column-end: span 3; - //background: $content_bg_color; - padding: $spc; - border-radius: $panel_radius 0 0 $panel_radius; - display: flex; - align-items: center; - justify-content: center; - //background: url('../../../sprites/splotchy.svg'); - // background-size: cover; - //@include outer_shadow(); -} - -.area_right { - grid-column-end: span 1; - background: $content_bg_secondary; - padding: $spc; - border-radius: $panel_radius 0 0 $panel_radius; - user-select: none; - - @include outer_shadow(); -} - -.area_sign { - font-size: $text_sign; - font-weight: 800; - line-height: 1.2em; -} - -.inputs { - display: flex; - align-items: stretch; - flex-direction: column; -} diff --git a/src/components/main/Header/index.tsx b/src/components/main/Header/index.tsx index 91a85299..0c3128c2 100644 --- a/src/components/main/Header/index.tsx +++ b/src/components/main/Header/index.tsx @@ -1,29 +1,23 @@ import * as React from "react"; import { Logo } from "~/components/main/Logo"; import { connect } from "react-redux"; -import { IUserState } from "~/redux/user/reducer"; import { push as historyPush } from "connected-react-router"; import * as style from "./style.scss"; import { Filler } from "~/components/containers/Filler"; import { Link } from "react-router-dom"; +import {selectUser} from "~/redux/auth/selectors"; +import {Group} from "~/components/containers/Group"; -const mapStateToProps = ({ - user: { - profile: { username, is_user } - } -}: { - user: IUserState; -}) => ({ username, is_user }); +const mapStateToProps = selectUser; const mapDispatchToProps = { push: historyPush }; -type IHeaderProps = ReturnType & - typeof mapDispatchToProps & {}; +type IProps = ReturnType & typeof mapDispatchToProps & {}; -export const Component: React.FunctionComponent = ({ +const HeaderUnconnected: React.FunctionComponent = ({ username, is_user }) => { @@ -43,16 +37,18 @@ export const Component: React.FunctionComponent = ({ - {/* +
username
- */} +
); }; -export const Header = connect( +const Header = connect( mapStateToProps, mapDispatchToProps -)(Component); +)(HeaderUnconnected); + +export { Header }; diff --git a/src/constants/api.ts b/src/constants/api.ts index b0629e42..0ceacee3 100644 --- a/src/constants/api.ts +++ b/src/constants/api.ts @@ -1,6 +1,6 @@ export const API = { - BASE: 'http://localhost:3000', + BASE: 'http://localhost:3333', USER: { - LOGIN: '/user/login', + LOGIN: '/auth/login', } }; diff --git a/src/containers/App.tsx b/src/containers/App.tsx index 92f47525..834ae8ca 100644 --- a/src/containers/App.tsx +++ b/src/containers/App.tsx @@ -6,7 +6,6 @@ import { ConnectedRouter } from "connected-react-router"; import { history } from "~/redux/store"; import { NavLink, Switch, Route, Redirect } from "react-router-dom"; import { FlowLayout } from "~/containers/flow/FlowLayout"; -import { LoginLayout } from "~/containers/login/LoginLayout"; import { MainLayout } from "~/containers/main/MainLayout"; import { ImageExample } from "~/containers/examples/ImageExample"; import { EditorExample } from "~/containers/examples/EditorExample"; @@ -15,7 +14,7 @@ import { Sprites } from "~/sprites/Sprites"; import { URLS } from "~/constants/urls"; import { Modal } from "~/containers/dialogs/Modal"; import { selectModal } from "~/redux/modal/selectors"; -import { BlurWrapper } from "../components/containers/BlurWrapper/index"; +import { BlurWrapper } from "~/components/containers/BlurWrapper"; const mapStateToProps = selectModal; const mapDispatchToProps = {}; @@ -37,8 +36,6 @@ class Component extends React.Component { - - diff --git a/src/containers/dialogs/LoginDialog/index.tsx b/src/containers/dialogs/LoginDialog/index.tsx index a4f3f52f..544cf11a 100644 --- a/src/containers/dialogs/LoginDialog/index.tsx +++ b/src/containers/dialogs/LoginDialog/index.tsx @@ -1,4 +1,4 @@ -import React, { FC, useState } from 'react'; +import React, {FC, FormEvent, useCallback, useEffect, useState} from 'react'; import { ScrollDialog } from '../ScrollDialog'; import { IDialogProps } from '~/redux/modal/constants'; import { useCloseOnEscape } from '~/utils/hooks'; @@ -7,12 +7,32 @@ import { InputText } from '~/components/input/InputText'; import { Button } from '~/components/input/Button'; import { Padder } from '~/components/containers/Padder'; import * as styles from './styles.scss'; -type IProps = IDialogProps & {}; +import {selectAuthLogin} from "~/redux/auth/selectors"; +import * as ACTIONS from '~/redux/auth/actions'; +import {connect} from "react-redux"; -const LoginDialog: FC = ({ onRequestClose }) => { +const mapStateToProps = selectAuthLogin; + +const mapDispatchToProps = { + userSendLoginRequest: ACTIONS.userSendLoginRequest, + userSetLoginError: ACTIONS.userSetLoginError, +}; + +type IProps = ReturnType & typeof mapDispatchToProps & IDialogProps & {}; + +const LoginDialogUnconnected: FC = ({ onRequestClose, error , userSendLoginRequest, userSetLoginError }) => { const [username, setUserName] = useState(''); const [password, setPassword] = useState(''); + const onSubmit = useCallback((event: FormEvent) => { + event.preventDefault(); + userSendLoginRequest({ username, password }); + }, [userSendLoginRequest, username, password]); + + useEffect(() => { + if (error) userSetLoginError(null); + }, [username, password]); + const buttons = ( @@ -23,23 +43,29 @@ const LoginDialog: FC = ({ onRequestClose }) => { useCloseOnEscape(onRequestClose); + console.log({ error }); + return ( - - -
- -

РЕШИТЕЛЬНО ВОЙТИ

+
+ + +
+ +

РЕШИТЕЛЬНО ВОЙТИ

-
-
+
+
- - - -
- - + + + +
+ + + ); }; +const LoginDialog = connect(mapStateToProps, mapDispatchToProps)(LoginDialogUnconnected); + export { LoginDialog }; diff --git a/src/containers/login/LoginLayout/index.tsx b/src/containers/login/LoginLayout/index.tsx deleted file mode 100644 index af6fa6cb..00000000 --- a/src/containers/login/LoginLayout/index.tsx +++ /dev/null @@ -1,18 +0,0 @@ -import * as React from 'react'; -import { LoginForm } from '~/components/login/LoginForm'; -import { Header } from "~/components/main/Header"; -import { GodRays } from "~/components/main/GodRays"; - -import * as styles from './styles.scss'; - -export const LoginLayout: React.FunctionComponent<{}> = () => ( -
- -
-
-
-
- -
-
-); diff --git a/src/containers/login/LoginLayout/styles.scss b/src/containers/login/LoginLayout/styles.scss deleted file mode 100644 index 36787539..00000000 --- a/src/containers/login/LoginLayout/styles.scss +++ /dev/null @@ -1,30 +0,0 @@ -.wrapper { - min-height: 100vh; - display: flex; - justify-content: center; - flex-direction: column; - align-items: stretch; -} - -.header {} - -.container { - flex: 1; - display: flex; - justify-content: center; - align-items: center; - justify-self: stretch; -} - -.form { - width: $content_width; - min-height: $cell * 2; - box-sizing: border-box; - border-radius: $panel_radius; - display: flex; - align-items: stretch; - justify-content: stretch; - // background: rgba(0,0,0,0.1); - - // @include outer_shadow(); -} diff --git a/src/redux/auth/actions.ts b/src/redux/auth/actions.ts new file mode 100644 index 00000000..d816f1df --- /dev/null +++ b/src/redux/auth/actions.ts @@ -0,0 +1,18 @@ +import { AUTH_USER_ACTIONS } from "~/redux/auth/constants"; +import {IAuthState, IUser} from "~/redux/auth/types"; + +export const userSendLoginRequest = ({ + username, password +}: { + username: string, password: string +}) => ({ type: AUTH_USER_ACTIONS.SEND_LOGIN_REQUEST, username, password }); + +export const userSetLoginError = (error: IAuthState['login']['error']) => ({ + type: AUTH_USER_ACTIONS.SET_LOGIN_ERROR, error +}); + +export const authSetToken = (token: IAuthState['token']) => ({ + type: AUTH_USER_ACTIONS.SET_TOKEN, token, +}); + +export const authSetUser = (profile: Partial) => ({ type: AUTH_USER_ACTIONS.SET_USER, profile }); diff --git a/src/redux/auth/api.ts b/src/redux/auth/api.ts new file mode 100644 index 00000000..dd0385b9 --- /dev/null +++ b/src/redux/auth/api.ts @@ -0,0 +1,15 @@ +import {api, authMiddleware, errorMiddleware, resultMiddleware} from "~/utils/api"; +import { API } from "~/constants/api"; +import { IApiUser } from "~/redux/auth/constants"; +import {IResultWithStatus} from "~/redux/types"; +import {userLoginTransform} from "~/redux/auth/transforms"; + +export const apiUserLogin = ( + { username, password }: + { username: string, password: string } +): Promise> => ( + api.post(API.USER.LOGIN, { username, password }) + .then(resultMiddleware) + .catch(errorMiddleware) + .then(userLoginTransform) +); diff --git a/src/redux/user/constants.ts b/src/redux/auth/constants.ts similarity index 57% rename from src/redux/user/constants.ts rename to src/redux/auth/constants.ts index a5aa84d2..d8444efe 100644 --- a/src/redux/user/constants.ts +++ b/src/redux/auth/constants.ts @@ -1,7 +1,10 @@ -export const USER_ACTIONS = { +import {IToken, IUser} from "~/redux/auth/types"; + +export const AUTH_USER_ACTIONS = { SEND_LOGIN_REQUEST: 'SEND_LOGIN_REQUEST', SET_LOGIN_ERROR: 'SET_LOGIN_ERROR', SET_USER: 'SET_USER', + SET_TOKEN: 'SET_TOKEN', }; export const USER_ERRORS = { @@ -13,6 +16,28 @@ export const USER_STATUSES = { 404: USER_ERRORS.INVALID_CREDENTIALS, }; +export const USER_ROLES = { + GUEST: 'guest', + USER: 'user', + ADMIN: 'admin', +}; + +export const EMPTY_TOKEN: IToken = { + access: null, + refresh: null, +}; + +export const EMPTY_USER: IUser = { + id: null, + role: USER_ROLES.GUEST, + email: null, + name: null, + username: null, + photo: null, + is_activated: false, + is_user: false, +}; + export interface IApiUser { id: number, username: string, diff --git a/src/redux/auth/handlers.ts b/src/redux/auth/handlers.ts new file mode 100644 index 00000000..aefb6936 --- /dev/null +++ b/src/redux/auth/handlers.ts @@ -0,0 +1,36 @@ +import {AUTH_USER_ACTIONS} from "~/redux/auth/constants"; +import * as ActionCreators from "~/redux/auth/actions"; +import {IAuthState} from "~/redux/auth/types"; + +interface ActionHandler { + (state: IAuthState, payload: T extends (...args: any[]) => infer R ? R : any): IAuthState; +} + +const setLoginError: ActionHandler = ( + state, + { error } +) => ({ + ...state, + login: { + ...state.login, + error, + } +}); + +const setUser: ActionHandler = (state, { profile }) => ({ + ...state, + user: { + ...state.user, + ...profile + } +}); + +const setToken: ActionHandler = (state, { token }) => ({ + ...state, token, +}); + +export const AUTH_USER_HANDLERS = { + [AUTH_USER_ACTIONS.SET_LOGIN_ERROR]: setLoginError, + [AUTH_USER_ACTIONS.SET_USER]: setUser, + [AUTH_USER_ACTIONS.SET_TOKEN]: setToken, +}; diff --git a/src/redux/auth/reducer.ts b/src/redux/auth/reducer.ts new file mode 100644 index 00000000..766b0b94 --- /dev/null +++ b/src/redux/auth/reducer.ts @@ -0,0 +1,19 @@ +import {EMPTY_TOKEN, EMPTY_USER, AUTH_USER_ACTIONS} from "~/redux/auth/constants"; +import { createReducer } from "~/utils/reducer"; +import {IAuthState} from "~/redux/auth/types"; +import {AUTH_USER_HANDLERS} from "~/redux/auth/handlers"; + +const HANDLERS = { + ...AUTH_USER_HANDLERS, +}; + +const INITIAL_STATE: IAuthState = { + token: { ...EMPTY_TOKEN }, + user: { ...EMPTY_USER }, + login: { + error: null, + is_loading: false, + }, +}; + +export default createReducer(INITIAL_STATE, HANDLERS); diff --git a/src/redux/auth/sagas.ts b/src/redux/auth/sagas.ts new file mode 100644 index 00000000..9805d638 --- /dev/null +++ b/src/redux/auth/sagas.ts @@ -0,0 +1,33 @@ +import {call, put, takeLatest } from 'redux-saga/effects'; +import { SagaIterator } from 'redux-saga'; +import {AUTH_USER_ACTIONS} from "~/redux/auth/constants"; +import * as ActionCreators from '~/redux/auth/actions'; +import {authSetToken, userSetLoginError} from "~/redux/auth/actions"; +import {apiUserLogin} from "~/redux/auth/api"; + +function* sendLoginRequestSaga({ username, password }: ReturnType): SagaIterator { + if (!username || !password) return; + + const { error, data: { access, refresh, user }} = yield call(apiUserLogin, { username, password }); + + console.log({ access, refresh, user, error }); + + if (error) return yield put(userSetLoginError(error)); + + yield put(authSetToken({ access, refresh })); + // const { token, status, user }: + // { token: string, status: number, user: IApiUser } = yield call(apiUserLogin, { username, password }); + // + // if (!token) return yield put(userSetLoginError({ error: USER_STATUSES[status] || USER_ERRORS.INVALID_CREDENTIALS })); + // + // const { id, role, email, activated: is_activated } = user; + // + // yield put(userSetUser({ token, id, role, email, username: user.username, is_activated, is_user: true })); + // yield put(push('/')); +} + +function* mySaga() { + yield takeLatest(AUTH_USER_ACTIONS.SEND_LOGIN_REQUEST, sendLoginRequestSaga); +} + +export default mySaga; diff --git a/src/redux/auth/selectors.ts b/src/redux/auth/selectors.ts new file mode 100644 index 00000000..98570e98 --- /dev/null +++ b/src/redux/auth/selectors.ts @@ -0,0 +1,4 @@ +import {IState} from "~/redux/store"; + +export const selectUser = (state: IState): IState['auth']['user'] => state.auth.user; +export const selectAuthLogin = (state: IState): IState['auth']['login'] => state.auth.login; diff --git a/src/redux/auth/transforms.ts b/src/redux/auth/transforms.ts new file mode 100644 index 00000000..e2c39f18 --- /dev/null +++ b/src/redux/auth/transforms.ts @@ -0,0 +1,14 @@ +import {IResultWithStatus} from "~/redux/types"; + +export const userLoginTransform = ({ status, data,error }: IResultWithStatus): IResultWithStatus => { + switch(true) { + case status === 401 || !data.access || data.refresh: + return { status, data, error: 'Пользователь не найден' }; + + case status === 200: + return { status, data, error: null }; + + default: + return { status, data, error: error || 'Неизвестная ошибка' }; + } +} diff --git a/src/redux/auth/types.ts b/src/redux/auth/types.ts new file mode 100644 index 00000000..eb07a06e --- /dev/null +++ b/src/redux/auth/types.ts @@ -0,0 +1,26 @@ +export interface IToken { + access: string; + refresh: string; +} + +export interface IUser { + id: number; + username: string; + email: string; + role: string; + photo: string; + name: string; + + is_activated: boolean; + is_user: boolean; +} + +export type IAuthState = Readonly<{ + user: IUser; + token: IToken; + + login: { + error: string; + is_loading: boolean; + }; +}>; diff --git a/src/redux/modal/reducer.ts b/src/redux/modal/reducer.ts index f1cd2b78..f6b830c1 100644 --- a/src/redux/modal/reducer.ts +++ b/src/redux/modal/reducer.ts @@ -10,7 +10,7 @@ export interface IModalState { const INITIAL_STATE: IModalState = { is_shown: true, - dialog: DIALOGS.TEST, + dialog: DIALOGS.LOGIN, }; export default createReducer(INITIAL_STATE, MODAL_HANDLERS); diff --git a/src/redux/store.ts b/src/redux/store.ts index 22ec4e99..918c1a2a 100644 --- a/src/redux/store.ts +++ b/src/redux/store.ts @@ -8,8 +8,10 @@ import { createBrowserHistory } from "history"; import { PersistConfig, Persistor } from "redux-persist/es/types"; import { routerMiddleware } from "connected-react-router"; -import userReducer, { IUserState } from "~/redux/user/reducer"; -import userSaga from "~/redux/user/sagas"; +import userReducer from "~/redux/auth/reducer"; +import userSaga from "~/redux/auth/sagas"; + +import { IAuthState } from "~/redux/auth/types"; import modalReducer, { IModalState } from "~/redux/modal/reducer"; import { IState } from "~/redux/store"; @@ -21,7 +23,7 @@ const userPersistConfig: PersistConfig = { }; export interface IState { - user: IUserState; + auth: IAuthState; modal: IModalState; router: RouterState; } @@ -35,15 +37,15 @@ const composeEnhancers = : compose; export const store = createStore( - combineReducers({ - user: persistReducer(userPersistConfig, userReducer), + combineReducers({ + auth: persistReducer(userPersistConfig, userReducer), modal: modalReducer, router: connectRouter(history) }), composeEnhancers(applyMiddleware(routerMiddleware(history), sagaMiddleware)) ); -export function configureStore(): { store: Store; persistor: Persistor } { +export function configureStore(): { store: Store; persistor: Persistor } { sagaMiddleware.run(userSaga); const persistor = persistStore(store); diff --git a/src/redux/types.ts b/src/redux/types.ts index 6aa2ca05..db7d586b 100644 --- a/src/redux/types.ts +++ b/src/redux/types.ts @@ -31,3 +31,9 @@ export interface IDialogProps { onRequestClose: () => void; onDialogChange: (dialog: ValueOf) => void; } + +export interface IResultWithStatus { + status: number; + data: T; + error?: string; +} diff --git a/src/redux/user/actions.ts b/src/redux/user/actions.ts deleted file mode 100644 index 3de9721e..00000000 --- a/src/redux/user/actions.ts +++ /dev/null @@ -1,16 +0,0 @@ -import { USER_ACTIONS } from "~/redux/user/constants"; -import { IUserProfile } from "~/redux/user/reducer"; - -export const userSendLoginRequest = ({ - username, password -}: { - username: string, password: string -}) => ({ type: USER_ACTIONS.SEND_LOGIN_REQUEST, username, password }); - -export const userSetLoginError = ({ - error -}: { - error: string -}) => ({ type: USER_ACTIONS.SET_LOGIN_ERROR, error }); - -export const userSetUser = (profile: Partial) => ({ type: USER_ACTIONS.SET_USER, profile }); diff --git a/src/redux/user/api.ts b/src/redux/user/api.ts deleted file mode 100644 index 104a74f3..00000000 --- a/src/redux/user/api.ts +++ /dev/null @@ -1,12 +0,0 @@ -import { api, authMiddleware } from "~/utils/api"; -import { API } from "~/constants/api"; -import { IApiUser } from "~/redux/user/constants"; - -export const apiUserLogin = ( - { username, password }: - { username: string, password: string } -): Promise<{ token: string, status?: number, user?: IApiUser }> => ( - api.post(API.USER.LOGIN, { username, password }) - .then(r => r && r.data && { token: r.data.token, user: r.data.user, status: 200 }) - .catch( (r) => ({ token: '', user: null, status: parseInt(r.response.status) })) -); diff --git a/src/redux/user/reducer.ts b/src/redux/user/reducer.ts deleted file mode 100644 index ac013cee..00000000 --- a/src/redux/user/reducer.ts +++ /dev/null @@ -1,76 +0,0 @@ -import * as ActionCreators from "~/redux/user/actions"; -import { USER_ACTIONS } from "~/redux/user/constants"; -import { createReducer } from "~/utils/reducer"; - -export interface IUserProfile { - id: number; - username: string; - email: string; - role: string; - token: string; - - is_activated: boolean; - is_user: boolean; -} - -export interface IUserFormStateLogin { - error: string; -} - -export type IUserState = Readonly<{ - profile: IUserProfile; - form_state: { - login: IUserFormStateLogin; - }; -}>; - -type UnsafeReturnType = T extends (...args: any[]) => infer R ? R : any; -interface ActionHandler { - (state: IUserState, payload: UnsafeReturnType): IUserState; -} - -const setLoginErrorHandler: ActionHandler = ( - state, - { error } -) => ({ - ...state, - form_state: { - ...state.form_state, - login: { - ...state.form_state.login, - error - } - } -}); - -const setUserHandler: ActionHandler = (state, { profile }) => ({ - ...state, - profile: { - ...state.profile, - ...profile - } -}); - -const HANDLERS = { - [USER_ACTIONS.SET_LOGIN_ERROR]: setLoginErrorHandler, - [USER_ACTIONS.SET_USER]: setUserHandler -}; - -const INITIAL_STATE: IUserState = { - profile: { - id: 0, - username: "", - email: "", - role: "", - token: "", - is_activated: false, - is_user: false - }, - form_state: { - login: { - error: "" - } - } -}; - -export default createReducer(INITIAL_STATE, HANDLERS); diff --git a/src/redux/user/sagas.ts b/src/redux/user/sagas.ts deleted file mode 100644 index bd530c0e..00000000 --- a/src/redux/user/sagas.ts +++ /dev/null @@ -1,27 +0,0 @@ -import { call, put, takeLatest } from 'redux-saga/effects'; -import { SagaIterator } from 'redux-saga'; -import { IApiUser, USER_ACTIONS, USER_ERRORS, USER_STATUSES } from "~/redux/user/constants"; -import * as ActionCreators from '~/redux/user/actions'; -import { apiUserLogin } from "~/redux/user/api"; -import { userSetLoginError, userSetUser } from "~/redux/user/actions"; -import { push } from 'connected-react-router' - -function* sendLoginRequestSaga({ username, password }: ReturnType): SagaIterator { - if (!username || !password) return yield put(userSetLoginError({ error: USER_ERRORS.EMPTY_CREDENTIALS })); - - const { token, status, user }: - { token: string, status: number, user: IApiUser } = yield call(apiUserLogin, { username, password }); - - if (!token) return yield put(userSetLoginError({ error: USER_STATUSES[status] || USER_ERRORS.INVALID_CREDENTIALS })); - - const { id, role, email, activated: is_activated } = user; - - yield put(userSetUser({ token, id, role, email, username: user.username, is_activated, is_user: true })); - yield put(push('/')); -} - -function* mySaga() { - yield takeLatest(USER_ACTIONS.SEND_LOGIN_REQUEST, sendLoginRequestSaga); -} - -export default mySaga; diff --git a/src/styles/inputs.scss b/src/styles/inputs.scss index 87c4b0f9..11419643 100644 --- a/src/styles/inputs.scss +++ b/src/styles/inputs.scss @@ -18,7 +18,7 @@ position: absolute; width: $gap * 2; height: $input_height; - top: 0; + top: 1px; right: 1px; transform: translateX(0); transition: transform 0.25s; @@ -282,11 +282,12 @@ color: $red; span { - background: white; + background: $content_bg; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; - padding: 0 5px; + padding: 0 2px; + border-radius: $radius; } } diff --git a/src/utils/api/index.ts b/src/utils/api/index.ts index d0417e75..84937899 100644 --- a/src/utils/api/index.ts +++ b/src/utils/api/index.ts @@ -11,3 +11,6 @@ export const authMiddleware = r => { export const api = axios.create({ baseURL: API.BASE, }); + +export const resultMiddleware = ({ status, data }) => ({ status, data }); +export const errorMiddleware = ({ status, data, response }) => ({ status, data: data || { response } });