Added more views and atlas APIs

This commit is contained in:
Jori Lallo
2016-05-07 11:52:08 -07:00
parent 84ba65f72a
commit cbe9c0b6ee
22 changed files with 397 additions and 27 deletions

View File

@@ -106,6 +106,7 @@ const text = (state = textDefaultState, action) => {
import team from './team';
import user from './user';
import atlases from './atlases';
export default combineReducers({
activeEditors,
@@ -113,4 +114,5 @@ export default combineReducers({
text,
user,
team,
atlases,
});

View File

@@ -0,0 +1,52 @@
import makeActionCreator from '../utils/actions';
import { client } from 'utils/ApiClient';
export const FETCH_ATLASES_PENDING = 'FETCH_ATLASES_PENDING';
export const FETCH_ATLASES_SUCCESS = 'FETCH_ATLASES_SUCCESS';
export const FETCH_ATLASES_FAILURE = 'FETCH_ATLASES_FAILURE';
const fetchAtlasesPending = makeActionCreator(FETCH_ATLASES_PENDING);
const fetchAtlasesSuccess = makeActionCreator(FETCH_ATLASES_SUCCESS, 'items', 'pagination');
const fetchAtlasesFailure = makeActionCreator(FETCH_ATLASES_FAILURE, 'error');
export function fetchAtlasesAsync(teamId) {
return (dispatch) => {
dispatch(fetchAtlasesPending());
client.post('/atlases.list', {
teamId: teamId,
})
.then(data => {
dispatch(fetchAtlasesSuccess(data.data, data.pagination));
})
.catch((err) => {
dispatch(fetchAtlasesFailure(err));
})
};
};
export const FETCH_ATLAS_PENDING = 'FETCH_ATLAS_PENDING';
export const FETCH_ATLAS_SUCCESS = 'FETCH_ATLAS_SUCCESS';
export const FETCH_ATLAS_FAILURE = 'FETCH_ATLAS_FAILURE';
const fetchAtlasPending = makeActionCreator(FETCH_ATLAS_PENDING);
const fetchAtlasSuccess = makeActionCreator(FETCH_ATLAS_SUCCESS, 'data');
const fetchAtlasFailure = makeActionCreator(FETCH_ATLAS_FAILURE, 'error');
export function fetchAtlasAsync(atlasId) {
return (dispatch) => {
dispatch(fetchAtlasPending());
client.post('/atlases.info', {
id: atlasId,
})
.then(data => {
dispatch(fetchAtlasSuccess(data.data,));
})
.catch((err) => {
dispatch(fetchAtlasFailure(err));
})
};
};

View File

@@ -0,0 +1,23 @@
import React from 'react';
import Link from 'react-router/lib/Link';
import styles from './AtlasPreview.scss';
import classNames from 'classnames/bind';
const cx = classNames.bind(styles);
class AtlasPreview extends React.Component {
static propTypes = {
data: React.PropTypes.object.isRequired,
}
render() {
return (
<div className={ styles.container }>
<h2><Link to={ `/atlas/${this.props.data.id}` } className={ styles.atlasLink }>{ this.props.data.name }</Link></h2>
<div>No documents. Why not <Link to='/new-document'>create one</Link>?</div>
</div>
);
}
};
export default AtlasPreview;

View File

@@ -0,0 +1,6 @@
@import '../../utils/constants.scss';
.atlasLink {
text-decoration: none;
color: $textColor;
}

View File

@@ -0,0 +1,2 @@
import AtlasPreview from './AtlasPreview';
export default AtlasPreview;

View File

@@ -0,0 +1,27 @@
import React from 'react';
import styles from './CenteredContent.scss';
const CenteredContent = (props) => {
const style = {
maxWidth: props.maxWidth,
...props.style,
};
return (
<div className={ styles.content } style={ style }>
{ props.children }
</div>
);
};
CenteredContent.defaultProps = {
maxWidth: '600px',
};
CenteredContent.propTypes = {
children: React.PropTypes.node.isRequired,
style: React.PropTypes.object,
};
export default CenteredContent;

View File

@@ -0,0 +1,5 @@
.content {
display: flex;
flex: 1;
margin: 40px 20px;
}

View File

@@ -0,0 +1,2 @@
import CenteredContent from './CenteredContent';
export default CenteredContent;

View File

@@ -1,5 +1,6 @@
import React from 'react';
import { connect } from 'react-redux';
import Link from 'react-router/lib/Link';
import HeaderMenu from './components/HeaderMenu';
@@ -14,7 +15,9 @@ class Layout extends React.Component {
return (
<div className={ styles.container }>
<div className={ styles.header }>
<div className={ styles.teamName }>Coinbase</div>
<div className={ styles.teamName }>
<Link to="/">{ this.props.teamName }</Link>
</div>
<HeaderMenu>
<img src={ this.props.avatarUrl } />
</HeaderMenu>
@@ -29,6 +32,7 @@ class Layout extends React.Component {
const mapStateToProps = (state) => {
return {
teamName: state.team ? state.team.name : null,
avatarUrl: state.user ? state.user.avatarUrl : null,
}
};

View File

@@ -1,3 +1,5 @@
@import '../../utils/constants.scss';
.container {
display: flex;
flex: 1;
@@ -16,9 +18,11 @@
border-bottom: 1px solid #eee;
}
.teamName {
.teamName a {
font-family: 'Atlas Grotesk';
font-weight: bold;
color: $textColor;
text-decoration: none;
}
.content {

View File

@@ -22,6 +22,7 @@ import 'fonts/atlas/atlas.css';
import Home from 'scenes/Home';
// import App from 'scenes/App';
import Dashboard from 'scenes/Dashboard';
import Atlas from 'scenes/Atlas';
import SlackAuth from 'scenes/SlackAuth';
// Redux
@@ -47,12 +48,10 @@ persistStore(store, {
<Route path="/">
<IndexRoute component={Home} />
<Route path="/dashboard" component={Dashboard
} onEnter={ requireAuth } />
<Route path="/atlas/:id" component={Dashboard} onEnter={ requireAuth } />
<Route path="/atlas/:id/new" component={Dashboard} onEnter={ requireAuth } />
<Route path="/dashboard" component={ Dashboard } onEnter={ requireAuth } />
<Route path="/atlas/:id" component={ Atlas } onEnter={ requireAuth } />
<Route path="/editor" component={Dashboard} />
<Route path="/editor" component={Dashboard} onEnter={ requireAuth } />
<Route path="/auth/slack" component={SlackAuth} />
</Route>
@@ -69,4 +68,3 @@ function requireAuth(nextState, replace) {
});
}
}

41
src/reducers/atlases.js Normal file
View File

@@ -0,0 +1,41 @@
import {
FETCH_ATLASES_PENDING,
FETCH_ATLASES_SUCCESS,
FETCH_ATLASES_FAILURE,
} from 'actions/AtlasActions';
const initialState = {
items: [],
pagination: null,
isLoading: false,
}
const atlases = (state = initialState, action) => {
switch (action.type) {
case FETCH_ATLASES_PENDING: {
return {
...state,
isLoading: true,
};
}
case FETCH_ATLASES_SUCCESS: {
return {
...state,
items: action.items,
pagination: action.pagination,
isLoading: false,
};
}
case FETCH_ATLASES_FAILURE: {
return {
...state,
isLoading: false,
error: action.error,
};
}
default:
return state;
}
};
export default atlases;

85
src/scenes/Atlas/Atlas.js Normal file
View File

@@ -0,0 +1,85 @@
import React from 'react';
import { connect } from 'react-redux';
import { bindActionCreators } from 'redux';
import { replace } from 'react-router-redux';
import { fetchAtlasAsync } from 'actions/AtlasActions';
// Temp
import { client } from 'utils/ApiClient';
import Layout from 'components/Layout';
import AtlasPreviewLoading from 'components/AtlasPreviewLoading';
import CenteredContent from 'components/CenteredContent';
import styles from './Atlas.scss';
class Atlas extends React.Component {
static propTypes = {
atlas: React.PropTypes.object,
}
state = {
isLoading: true,
data: null,
}
componentDidMount = () => {
const { id } = this.props.params;
// this.props.fetchAtlasAsync(id);
// Temp before breaking out redux store
client.post('/atlases.info', {
id: id,
})
.then(data => {
this.setState({
isLoading: false,
data: data.data
});
})
}
render() {
const data = this.state.data;
return (
<Layout>
<CenteredContent>
{ this.state.isLoading ? (
<AtlasPreviewLoading />
) : (
<div className={ styles.container }>
<div className={ styles.atlasDetails }>
<h2>{ data.name }</h2>
<blockquote>
{ data.description }
</blockquote>
</div>
<div className={ styles.divider }><span></span></div>
</div>
) }
</CenteredContent>
</Layout>
);
}
}
const mapStateToProps = (state) => {
return {
isLoading: state.atlases.isLoading,
}
};
const mapDispatchToProps = (dispatch) => {
return bindActionCreators({
replace,
fetchAtlasAsync,
}, dispatch)
}
export default connect(
mapStateToProps,
mapDispatchToProps
)(Atlas);

View File

@@ -0,0 +1,31 @@
.container {
display: flex;
flex: 1;
flex-direction: column;
}
.atlasDetails {
display: flex;
flex: 1;
flex-direction: column;
blockquote {
padding: 0;
margin: 0 0 20px 0;
font-style: italic;
}
}
.divider {
display: flex;
flex: 1;
justify-content: center;
span {
display: flex;
width: 50%;
margin: 20px 0;
border-bottom: 1px solid #eee;
}
}

View File

@@ -0,0 +1,2 @@
import Atlas from './Atlas';
export default Atlas;

View File

@@ -1,37 +1,53 @@
import React from 'react';
import { connect } from 'react-redux';
import { bindActionCreators } from 'redux';
import { replace } from 'react-router-redux';
import { client } from 'utils/ApiClient';
import { fetchAtlasesAsync } from 'actions/AtlasActions';
import Layout from 'components/Layout';
import AtlasPreview from 'components/AtlasPreview';
import AtlasPreviewLoading from 'components/AtlasPreviewLoading';
import CenteredContent from 'components/CenteredContent';
import styles from './Dashboard.scss';
class Dashboard extends React.Component {
static propTypes = {
replace: React.PropTypes.func.isRequired,
}
componentDidMount = () => {
this.props.fetchAtlasesAsync(this.props.teamId);
}
render() {
return (
<Layout
header={<div>header!</div>}
>
<div className={ styles.content }>
<AtlasPreviewLoading />
</div>
<Layout>
<CenteredContent>
{ this.props.isLoading ? (
<AtlasPreviewLoading />
) : this.props.items.map((item) => {
return (<AtlasPreview data={ item } />);
}) }
</CenteredContent>
</Layout>
);
}
}
const mapStateToProps = (state) => {
return {
teamId: state.team ? state.team.id : null,
isLoading: state.atlases.isLoading,
items: state.atlases.items,
}
};
const mapDispatchToProps = (dispatch) => {
return bindActionCreators({ replace }, dispatch)
return bindActionCreators({
fetchAtlasesAsync,
}, dispatch)
}
export default connect(
null, mapDispatchToProps
mapStateToProps,
mapDispatchToProps
)(Dashboard);

View File

@@ -1,7 +0,0 @@
.content {
display: flex;
flex: 1;
max-width: 600px;
margin: 40px 20px;
}