Add `header-layout` component (#671)
parent
191bf2761c
commit
d802642512
File diff suppressed because it is too large
Load Diff
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@ -0,0 +1 @@
|
||||
<svg width="98" height="27" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"><defs><path d="M101 0v29l-52.544.001C44.326 35.511 35.598 40 25.5 40 11.417 40 0 31.27 0 20.5S11.417 1 25.5 1c4.542 0 8.807.908 12.5 2.5V0h63z" id="a"/></defs><g transform="translate(-2 -1)" fill="none" fill-rule="evenodd"><mask id="b" fill="#fff"><use xlink:href="#a"/></mask><path d="M48.142 1c4.736 0 6.879 3.234 6.879 5.904v2.068h-4.737V6.904c0-.79-.789-2.144-2.142-2.144-1.654 0-2.368 1.354-2.368 2.144v15.192c0 .79.714 2.144 2.368 2.144 1.353 0 2.142-1.354 2.142-2.144v-2.068h4.737v2.068c0 2.67-2.143 5.904-6.88 5.904C42.956 28 41 24.766 41 22.134V6.904C41 4.234 42.955 1 48.142 1zM19-6c9.389 0 17 7.611 17 17s-7.611 17-17 17S2 20.389 2 11 9.611-6 19-6zm42.256 7.338l3.345 19.48h.075l3.42-19.48h5l-6.052 26.324h-5L56.22 1.338h5.037zm20.706 0l5.413 26.324h-4.699l-.94-6.13h-4.548l-.902 6.13h-4.435l5.413-26.324h4.698zm18.038 0v3.723h-4.849v22.6h-4.699v-22.6h-4.81V1.338H100zM19 4a7 7 0 1 0 0 14 7 7 0 0 0 0-14zm60.557 4.295h-.113l-1.466 9.439h3.007l-1.428-9.439z" fill="#000" fill-rule="nonzero" mask="url(#b)"/></g></svg>
|
||||
|
After Width: | Height: | Size: 1.1 KiB |
@ -0,0 +1 @@
|
||||
<svg width="90" height="78" xmlns="http://www.w3.org/2000/svg"><path d="M84.27 0c2.753 0 5.007 2.167 5.12 4.874l.005.215v67.148c0 2.734-2.183 4.972-4.908 5.085l-.217.004H5.123c-2.751 0-5.005-2.167-5.118-4.874L0 72.237V5.09C0 2.355 2.183.117 4.907.004L5.123 0H84.27zm1.58 16.242H3.546v55.995c0 .816.632 1.488 1.434 1.56l.144.007H84.27c.824 0 1.501-.627 1.574-1.424l.007-.143V16.242zM12.658 38.48h4.328c.59 0 1.076.452 1.138 1.031l.007.126v1.03h15.02v-1.03c0-.596.446-1.087 1.02-1.15l.125-.007h4.328c.59 0 1.076.451 1.138 1.03l.006.127v4.372c0 .596-.446 1.087-1.02 1.15l-.124.007h-1.02v10.548h1.019c.59 0 1.077.451 1.139 1.031l.006.126v4.372c0 .596-.446 1.087-1.02 1.15l-.125.007h-4.327a1.15 1.15 0 0 1-1.139-1.03l-.006-.127v-1.03H18.13v1.03c0 .596-.446 1.087-1.02 1.15l-.125.007h-4.328a1.15 1.15 0 0 1-1.138-1.03l-.007-.127v-4.372c0-.596.447-1.087 1.02-1.15l.125-.007h1.019V45.166h-1.02a1.15 1.15 0 0 1-1.138-1.03l-.006-.127v-4.372c0-.596.446-1.087 1.02-1.15l.125-.007h4.328zm3.183 19.548h-2.038v2.058h2.038v-2.058zm21.638 0h-2.039v2.058h2.039v-2.058zM33.15 42.98H18.13v1.03c0 .595-.447 1.087-1.02 1.15l-.125.006h-1.019v10.548h1.019c.59 0 1.076.451 1.138 1.031l.007.126V57.9h15.02V56.87c0-.596.446-1.087 1.02-1.15l.125-.007h1.019V45.166h-1.019a1.15 1.15 0 0 1-1.139-1.031l-.006-.126v-1.03zm21.575-7.62c.398 0 .72.338.72.755v.67h9.458v-.67c0-.417.323-.755.721-.755h2.725c.398 0 .72.338.72.754v2.852c0 .416-.322.754-.72.754h-.641v6.88h.64c.399 0 .722.337.722.754v2.852c0 .416-.323.754-.721.754h-2.725c-.398 0-.721-.338-.721-.754v-.672h-9.457v.672c0 .416-.322.754-.72.754H52c-.398 0-.72-.338-.72-.754v-2.852c0-.416.322-.754.72-.754h.642v-6.88H52c-.398 0-.72-.337-.72-.754v-2.851c0-.417.322-.755.72-.755zm-.72 12.749H52.72v1.342h1.283V48.11zm13.623 0h-1.283v1.342h1.283V48.11zm-2.725-9.814h-9.457v.671c0 .417-.323.755-.721.755h-.641V46.6h.641c.399 0 .721.337.721.754v.671h9.457v-.67c0-.417.323-.755.72-.755h.642v-6.88h-.64c-.4 0-.722-.338-.722-.754v-.671zm-49.063 2.5h-2.038v2.058h2.038v-2.058zm21.638-.001H35.44v2.058h2.038v-2.058zm30.15-3.925h-1.283v1.342h1.283V36.87zm-13.624 0h-1.283v1.343h1.283v-1.343zM9.098 19.76c.93 0 1.692.711 1.766 1.616l.006.145v.118c0 .973-.793 1.761-1.772 1.761-.93 0-1.693-.711-1.767-1.616l-.005-.145v-.118c0-.973.793-1.761 1.772-1.761zm21.65 0c.978 0 1.771.788 1.771 1.76 0 .974-.793 1.762-1.771 1.762H16.423c-.98 0-1.772-.788-1.772-1.761 0-.973.792-1.761 1.772-1.761h14.325zm13.988 0c.98 0 1.772.788 1.772 1.76 0 .974-.792 1.762-1.772 1.762h-5.29a1.768 1.768 0 0 1-1.772-1.761c0-.973.796-1.761 1.772-1.761h5.29zm9.868 0c.977 0 1.773.788 1.773 1.76 0 .974-.796 1.762-1.773 1.762H53.05a1.766 1.766 0 0 1-1.772-1.761c0-.973.793-1.761 1.772-1.761h1.553zm17.107 0c.977 0 1.772.788 1.772 1.76 0 .974-.795 1.762-1.772 1.762h-8.194c-.98 0-1.773-.788-1.773-1.761 0-.973.793-1.761 1.773-1.761h8.194zm12.56-16.238H5.122c-.82 0-1.5.628-1.572 1.424l-.006.143v7.631H85.85V5.09c0-.863-.709-1.567-1.58-1.567zM9.125 6.24c.98 0 1.772.787 1.772 1.76s-.793 1.761-1.772 1.761c-.977 0-1.8-.788-1.8-1.76 0-.974.761-1.761 1.741-1.761h.059zm7.354 0c.979 0 1.772.787 1.772 1.76s-.793 1.761-1.772 1.761c-.977 0-1.829-.788-1.829-1.76 0-.974.734-1.761 1.712-1.761h.117zm7.297 0c.977 0 1.773.787 1.773 1.76s-.796 1.761-1.773 1.761c-.979 0-1.8-.788-1.8-1.76 0-.974.764-1.761 1.743-1.761h.057z" fill="#9B9B9B" fill-rule="evenodd"/></svg>
|
||||
|
After Width: | Height: | Size: 3.3 KiB |
@ -0,0 +1,40 @@
|
||||
import { Dispatch, ActionCreator } from 'redux';
|
||||
|
||||
|
||||
export const getUsers = () => (dispatch: Dispatch) => {
|
||||
dispatch({
|
||||
type: 'GET_USERS',
|
||||
});
|
||||
}
|
||||
|
||||
export const getUsersSuccess = (users: [], isCurrentUser: boolean) => (dispatch: Dispatch) => {
|
||||
dispatch({
|
||||
type: 'GET_USERS_SUCCESS',
|
||||
payload: users,
|
||||
currentUser: isCurrentUser ? (users as any)[0] : isCurrentUser,
|
||||
});
|
||||
}
|
||||
|
||||
export const getUsersError = (error: {}) => (dispatch: Dispatch) => {
|
||||
dispatch({
|
||||
type: 'GET_USERS_ERROR',
|
||||
payload: error,
|
||||
});
|
||||
}
|
||||
|
||||
export const getUsersAsync = (filter = {}) => {
|
||||
return (dispatch: ActionCreator<Dispatch>) => {
|
||||
dispatch(getUsers());
|
||||
|
||||
return (window as any).cvat.users.get(filter).then(
|
||||
(users: any) => {
|
||||
dispatch(getUsersSuccess(users, (filter as any).self));
|
||||
},
|
||||
(error: any) => {
|
||||
dispatch(getUsersError(error));
|
||||
|
||||
throw error;
|
||||
},
|
||||
);
|
||||
};
|
||||
}
|
||||
@ -1,53 +0,0 @@
|
||||
import React, { PureComponent } from 'react';
|
||||
import { Location, Action } from 'history';
|
||||
|
||||
import * as queryString from 'query-string';
|
||||
|
||||
import setQueryObject from '../../utils/tasks-filter'
|
||||
|
||||
import { connect } from 'react-redux';
|
||||
import { getTasksAsync } from '../../actions/tasks.actions';
|
||||
import { filterTasks } from '../../actions/tasks-filter.actions';
|
||||
|
||||
import { Layout } from 'antd';
|
||||
|
||||
import DashboardHeader from './header/dashboard-header';
|
||||
import DashboardContent from './content/dashboard-content';
|
||||
import DashboardFooter from './footer/dashboard-footer';
|
||||
|
||||
import './dashboard-page.scss';
|
||||
|
||||
|
||||
class Dashboard extends PureComponent<any, any> {
|
||||
componentDidMount() {
|
||||
this.loadTasks(this.props.location.search);
|
||||
|
||||
this.props.history.listen((location: Location, action: Action) => {
|
||||
this.loadTasks(location.search);
|
||||
});
|
||||
}
|
||||
|
||||
render() {
|
||||
return (
|
||||
<Layout className="layout">
|
||||
<DashboardHeader />
|
||||
<DashboardContent />
|
||||
<DashboardFooter />
|
||||
</Layout>
|
||||
);
|
||||
}
|
||||
|
||||
private loadTasks = (params: any) => {
|
||||
const query = queryString.parse(params);
|
||||
const queryObject = setQueryObject(query);
|
||||
|
||||
this.props.dispatch(filterTasks(queryObject));
|
||||
this.props.dispatch(getTasksAsync(queryObject));
|
||||
}
|
||||
}
|
||||
|
||||
const mapStateToProps = (state: any) => {
|
||||
return { ...state.tasks, ...state.tasksFilter };
|
||||
};
|
||||
|
||||
export default connect(mapStateToProps)(Dashboard);
|
||||
@ -1,34 +0,0 @@
|
||||
.dashboard-header {
|
||||
min-width: 1024px;
|
||||
background: #001529;
|
||||
|
||||
&__logo {
|
||||
height: 64px;
|
||||
|
||||
.logo {
|
||||
color: white;
|
||||
}
|
||||
}
|
||||
&__search {
|
||||
text-align: center;
|
||||
|
||||
.search {
|
||||
max-width: 300px;
|
||||
}
|
||||
}
|
||||
&__actions {
|
||||
text-align: right;
|
||||
|
||||
.action:not(:nth-child(1)) {
|
||||
margin-left: 8px;
|
||||
}
|
||||
|
||||
.action {
|
||||
width: 100px;
|
||||
}
|
||||
}
|
||||
|
||||
.logo, .search, .action {
|
||||
vertical-align: middle;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,66 @@
|
||||
.header-layout {
|
||||
min-width: 1024px;
|
||||
height: 100%;
|
||||
padding: 0 16px;
|
||||
line-height: initial;
|
||||
background: #d8d8d8;
|
||||
|
||||
&__logo {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
|
||||
img {
|
||||
height: 18px;
|
||||
}
|
||||
}
|
||||
|
||||
&__menu {
|
||||
.ant-menu {
|
||||
font-size: 16px;
|
||||
color: black;
|
||||
background-color: #d8d8d8;
|
||||
line-height: 44px;
|
||||
border-bottom: none;
|
||||
|
||||
.ant-menu-item {
|
||||
border-bottom: 3px solid transparent;
|
||||
}
|
||||
|
||||
.last-menu-item {
|
||||
float: right;
|
||||
margin-right: 28px;
|
||||
}
|
||||
|
||||
.ant-menu-item-selected, .ant-menu-item-active {
|
||||
color: black !important;
|
||||
border-bottom: 3px solid black !important;
|
||||
background-color: #c3c3c3 !important;
|
||||
}
|
||||
|
||||
a, a:hover {
|
||||
color: black;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&__dropdown {
|
||||
border-left: 1px solid #c3c3c3;
|
||||
cursor: pointer;
|
||||
|
||||
display: flex;
|
||||
align-items: center;
|
||||
font-size: 16px;
|
||||
color: black;
|
||||
|
||||
i:first-child {
|
||||
margin-right: 12px;
|
||||
font-size: 18px;
|
||||
}
|
||||
|
||||
i:last-child {
|
||||
margin-left: auto;
|
||||
font-size: 18px;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1,11 +1,11 @@
|
||||
import React from 'react';
|
||||
import ReactDOM from 'react-dom';
|
||||
|
||||
import DashboardFooter from './dashboard-footer';
|
||||
import HeaderLayout from './header-layout';
|
||||
|
||||
|
||||
it('renders without crashing', () => {
|
||||
const div = document.createElement('div');
|
||||
ReactDOM.render(<DashboardFooter />, div);
|
||||
ReactDOM.render(<HeaderLayout />, div);
|
||||
ReactDOM.unmountComponentAtNode(div);
|
||||
});
|
||||
@ -0,0 +1,87 @@
|
||||
import React, { PureComponent } from 'react';
|
||||
|
||||
import { Link, withRouter } from 'react-router-dom';
|
||||
|
||||
import { connect } from 'react-redux';
|
||||
|
||||
import { logoutAsync } from '../../actions/auth.actions';
|
||||
|
||||
import { Layout, Row, Col, Menu, Dropdown, Icon } from 'antd';
|
||||
import { ClickParam } from 'antd/lib/menu';
|
||||
|
||||
import './header-layout.scss';
|
||||
|
||||
|
||||
const { Header } = Layout;
|
||||
|
||||
class HeaderLayout extends PureComponent<any, any> {
|
||||
hostUrl: string | undefined;
|
||||
|
||||
constructor(props: any) {
|
||||
super(props);
|
||||
|
||||
this.state = {
|
||||
selectedMenuItem: null,
|
||||
};
|
||||
|
||||
this.hostUrl = process.env.REACT_APP_API_HOST_URL;
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
this.setState({ selectedMenuItem: this.props.location.pathname.split('/')[1] });
|
||||
}
|
||||
|
||||
render() {
|
||||
const dropdownMenu = (
|
||||
<Menu>
|
||||
<Menu.Item onClick={ this.logout } key="logout">Logout</Menu.Item>
|
||||
</Menu>
|
||||
);
|
||||
|
||||
return (
|
||||
<Header className="header-layout">
|
||||
<Row type="flex" gutter={24}>
|
||||
<Col className="header-layout__logo" span={2}>
|
||||
<img src="./images/cvat-logo.svg" alt="CVAT logo" />
|
||||
</Col>
|
||||
<Col className="header-layout__menu" span={18}>
|
||||
<Menu onClick={ this.selectMenuItem } selectedKeys={ [this.state.selectedMenuItem] } mode="horizontal">
|
||||
<Menu.Item key="tasks">
|
||||
<Link to="/tasks">Tasks</Link>
|
||||
</Menu.Item>
|
||||
<Menu.Item disabled key="models">
|
||||
<Link to="/models">Models</Link>
|
||||
</Menu.Item>
|
||||
<Menu.Item disabled key="analitics">
|
||||
<Link to="/analitics">Analitics</Link>
|
||||
</Menu.Item>
|
||||
|
||||
<a className="last-menu-item" href={ `${this.hostUrl}/documentation/user_guide.html` } target="blank">Help</a>
|
||||
</Menu>
|
||||
</Col>
|
||||
<Dropdown className="header-layout__dropdown" overlay={ dropdownMenu } trigger={ ['click'] }>
|
||||
<Col span={4}>
|
||||
<Icon type="user" />
|
||||
{ this.props.currentUser ? <span>{ this.props.currentUser.username }</span> : null }
|
||||
<Icon type="caret-down" />
|
||||
</Col>
|
||||
</Dropdown>
|
||||
</Row>
|
||||
</Header>
|
||||
);
|
||||
}
|
||||
|
||||
private selectMenuItem = (event: ClickParam) => {
|
||||
this.setState({ selectedMenuItem: event.key });
|
||||
}
|
||||
|
||||
private logout = () => {
|
||||
this.props.dispatch(logoutAsync());
|
||||
}
|
||||
}
|
||||
|
||||
const mapStateToProps = (state: any) => {
|
||||
return { ...state.authContext, ...state.users };
|
||||
};
|
||||
|
||||
export default withRouter(connect(mapStateToProps)(HeaderLayout) as any);
|
||||
@ -1,6 +1,3 @@
|
||||
.not-found {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
.empty.not-found {
|
||||
height: 100vh;
|
||||
}
|
||||
|
||||
@ -1,11 +1,11 @@
|
||||
import React from 'react';
|
||||
import ReactDOM from 'react-dom';
|
||||
|
||||
import DashboardHeader from './dashboard-header';
|
||||
import TasksContent from './tasks-content';
|
||||
|
||||
|
||||
it('renders without crashing', () => {
|
||||
const div = document.createElement('div');
|
||||
ReactDOM.render(<DashboardHeader />, div);
|
||||
ReactDOM.render(<TasksContent />, div);
|
||||
ReactDOM.unmountComponentAtNode(div);
|
||||
});
|
||||
@ -1,4 +1,4 @@
|
||||
.dashboard-footer {
|
||||
.tasks-footer {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
@ -1,11 +1,11 @@
|
||||
import React from 'react';
|
||||
import ReactDOM from 'react-dom';
|
||||
|
||||
import DashboardContent from './dashboard-content';
|
||||
import TasksFooter from './tasks-footer';
|
||||
|
||||
|
||||
it('renders without crashing', () => {
|
||||
const div = document.createElement('div');
|
||||
ReactDOM.render(<DashboardContent />, div);
|
||||
ReactDOM.render(<TasksFooter />, div);
|
||||
ReactDOM.unmountComponentAtNode(div);
|
||||
});
|
||||
@ -0,0 +1,41 @@
|
||||
.tasks-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
width: 1024px;
|
||||
height: 90px;
|
||||
line-height: initial;
|
||||
margin: 0 auto;
|
||||
padding: 0;
|
||||
background-color: #f0f2f5;
|
||||
|
||||
&__logo {
|
||||
font-size: 20px;
|
||||
|
||||
.logo {
|
||||
margin: 0;
|
||||
color: black;
|
||||
}
|
||||
}
|
||||
|
||||
&__search {
|
||||
text-align: center;
|
||||
|
||||
.search {
|
||||
max-width: 300px;
|
||||
}
|
||||
}
|
||||
|
||||
&__actions {
|
||||
text-align: right;
|
||||
|
||||
.action:not(:nth-child(1)) {
|
||||
margin-left: 8px;
|
||||
}
|
||||
|
||||
.action {
|
||||
width: 180px;
|
||||
font-size: 16px;
|
||||
line-height: 19px;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,11 @@
|
||||
import React from 'react';
|
||||
import ReactDOM from 'react-dom';
|
||||
|
||||
import TasksHeader from './tasks-header';
|
||||
|
||||
|
||||
it('renders without crashing', () => {
|
||||
const div = document.createElement('div');
|
||||
ReactDOM.render(<TasksHeader />, div);
|
||||
ReactDOM.unmountComponentAtNode(div);
|
||||
});
|
||||
@ -1,11 +1,11 @@
|
||||
import React from 'react';
|
||||
import ReactDOM from 'react-dom';
|
||||
|
||||
import Dashboard from './dashboard-page';
|
||||
import TasksPage from './tasks-page';
|
||||
|
||||
|
||||
it('renders without crashing', () => {
|
||||
const div = document.createElement('div');
|
||||
ReactDOM.render(<Dashboard />, div);
|
||||
ReactDOM.render(<TasksPage />, div);
|
||||
ReactDOM.unmountComponentAtNode(div);
|
||||
});
|
||||
@ -0,0 +1,59 @@
|
||||
import React, { PureComponent } from 'react';
|
||||
import { Location, Action } from 'history';
|
||||
|
||||
import * as queryString from 'query-string';
|
||||
|
||||
import setQueryObject from '../../utils/tasks-filter'
|
||||
|
||||
import { connect } from 'react-redux';
|
||||
import { getTasksAsync } from '../../actions/tasks.actions';
|
||||
import { filterTasks } from '../../actions/tasks-filter.actions';
|
||||
|
||||
import { Layout, Spin } from 'antd';
|
||||
|
||||
import TasksHeader from './tasks-header/tasks-header';
|
||||
import TasksContent from './tasks-content/tasks-content';
|
||||
import TasksFooter from './tasks-footer/tasks-footer';
|
||||
|
||||
import './tasks-page.scss';
|
||||
|
||||
|
||||
class TasksPage extends PureComponent<any, any> {
|
||||
componentDidMount() {
|
||||
this.loadTasks(this.props.location.search);
|
||||
|
||||
this.props.history.listen(
|
||||
(location: Location, action: Action) => {
|
||||
if (location.pathname.includes('tasks')) {
|
||||
this.loadTasks(location.search);
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
render() {
|
||||
return (
|
||||
<Spin wrapperClassName="spinner" size="large" spinning={ this.props.isFetching }>
|
||||
<Layout className="layout">
|
||||
<TasksHeader />
|
||||
<TasksContent />
|
||||
<TasksFooter />
|
||||
</Layout>
|
||||
</Spin>
|
||||
);
|
||||
}
|
||||
|
||||
private loadTasks = (params: any) => {
|
||||
const query = queryString.parse(params);
|
||||
const queryObject = setQueryObject(query);
|
||||
|
||||
this.props.dispatch(filterTasks(queryObject));
|
||||
this.props.dispatch(getTasksAsync(queryObject));
|
||||
}
|
||||
}
|
||||
|
||||
const mapStateToProps = (state: any) => {
|
||||
return { ...state.authContext, ...state.tasks, ...state.tasksFilter };
|
||||
};
|
||||
|
||||
export default connect(mapStateToProps)(TasksPage);
|
||||
@ -1,15 +1,33 @@
|
||||
import { combineReducers } from 'redux';
|
||||
import { combineReducers, AnyAction } from 'redux';
|
||||
|
||||
import authContext from './auth.reducer';
|
||||
import tasks from './tasks.reducer';
|
||||
import users from './users.reducer';
|
||||
import tasksFilter from './tasks-filter.reducer';
|
||||
import server from './server.reducer';
|
||||
import annotations from './annotations.reducer';
|
||||
|
||||
|
||||
// TODO: investigate a better way to handle
|
||||
// INFO: global errors handler reducer
|
||||
const errorMessage = (state = null, action: AnyAction) => {
|
||||
const { type, payload } = action;
|
||||
|
||||
if (type === 'RESET_ERROR_MESSAGE') {
|
||||
return null;
|
||||
} else if (type.endsWith('ERROR')) {
|
||||
return payload;
|
||||
}
|
||||
|
||||
return state;
|
||||
}
|
||||
|
||||
export default combineReducers({
|
||||
authContext,
|
||||
tasks,
|
||||
users,
|
||||
tasksFilter,
|
||||
server,
|
||||
annotations,
|
||||
errorMessage,
|
||||
});
|
||||
|
||||
@ -0,0 +1,23 @@
|
||||
import { AnyAction } from 'redux';
|
||||
|
||||
|
||||
export default (
|
||||
state: any = {
|
||||
users: [],
|
||||
currentUser: null,
|
||||
isFetching: false,
|
||||
error: null,
|
||||
},
|
||||
action: AnyAction,
|
||||
) => {
|
||||
switch (action.type) {
|
||||
case 'GET_USERS':
|
||||
return { ...state, isFetching: true };
|
||||
case 'GET_USERS_SUCCESS':
|
||||
return { ...state, isFetching: false, users: Array.from(action.payload.values()), currentUser: action.currentUser };
|
||||
case 'GET_USERS_ERROR':
|
||||
return { ...state, isFetching: false, error: action.payload };
|
||||
default:
|
||||
return state;
|
||||
}
|
||||
}
|
||||
Loading…
Reference in New Issue