Initial commit

This commit is contained in:
Mathias Biilmann Christensen
2016-02-25 00:45:56 -08:00
commit c60d8ba706
19 changed files with 590 additions and 0 deletions

60
src/actions/config.js Normal file
View File

@ -0,0 +1,60 @@
import yaml from 'js-yaml';
export const CONFIG = {
REQUEST: 'REQUEST',
SUCCESS: 'SUCCESS',
FAILURE: 'FAILURE'
};
export function configLoaded(config) {
return {
type: CONFIG.SUCCESS,
payload: config
};
}
export function configLoading() {
return {
type: CONFIG.REQUEST
};
}
export function configFailed(err) {
return {
type: CONFIG.FAILURE,
error: 'Error loading config',
payload: err
};
}
export function loadConfig(config) {
if (window.CMS_CONFIG) {
return configLoaded(window.CMS_CONFIG);
}
return (dispatch, getState) => {
dispatch(configLoading());
fetch('config.yml').then((response) => {
if (response.status !== 200) {
throw `Failed to load config.yml (${response.status})`;
}
response.text().then(parseConfig).then((config) => dispatch(configLoaded(config)));
}).catch((err) => {
dispatch(configFailed(err));
});
};
}
function parseConfig(data) {
const config = yaml.safeLoad(data);
if (typeof CMS_ENV === 'string' && config[CMS_ENV]) {
for (var key in config[CMS_ENV]) {
if (config[CMS_ENV].hasOwnProperty(key)) {
config[key] = config[CMS_ENV][key];
}
}
}
return config;
}

58
src/containers/App.js Normal file
View File

@ -0,0 +1,58 @@
import React from 'react';
import { connect } from 'react-redux';
import { loadConfig } from '../actions/config';
class App extends React.Component {
constructor(props) {
super(props);
}
componentDidMount() {
this.props.dispatch(loadConfig());
}
configError(config) {
return <div>
<h1>Error loading the CMS configuration</h1>
<div>
<p>The "config.yml" file could not be loaded or failed to parse properly.</p>
<p><strong>Error message:</strong> {config.get('error')}</p>
</div>
</div>;
}
configLoading() {
return <div>
<h1>Loading configuration...</h1>
</div>;
}
render() {
const { config, children } = this.props;
if (config === null) {
return null;
}
if (config.get('error')) {
return this.configError(config);
}
if (config.get('isFetching')) {
return this.configLoading();
}
return (
<div>{children}</div>
);
}
}
function mapStateToProps(state) {
return {
config: state.config
};
}
export default connect(mapStateToProps)(App);

View File

@ -0,0 +1,26 @@
import React from 'react';
import { Link } from 'react-router';
import { connect } from 'react-redux';
class DashboardPage extends React.Component {
render() {
const { collections } = this.props;
return <div>
<h1>Dashboard</h1>
{collections && collections.map((collection) => (
<div key={collection.get('name')}>
<Link to={`/collections/${collection.get('name')}`}>{collection.get('name')}</Link>
</div>
)).toArray()}
</div>;
}
}
function mapStateToProps(state) {
return {
collections: state.collections
};
}
export default connect(mapStateToProps)(DashboardPage);

17
src/index.js Normal file
View File

@ -0,0 +1,17 @@
import React from 'react';
import { render } from 'react-dom';
import { Provider } from 'react-redux';
import configureStore from './store/configureStore';
import Routes from './routes/routes';
import 'file?name=index.html!../example/index.html';
const store = configureStore();
const el = document.createElement('div');
document.body.appendChild(el);
render((
<Provider store={store}>
<Routes/>
</Provider>
), el);

View File

@ -0,0 +1,16 @@
import Immutable from 'immutable';
import { CONFIG } from '../actions/config';
export function collections(state = null, action) {
switch (action.type) {
case CONFIG.SUCCESS:
const collections = action.payload && action.payload.collections;
return Immutable.OrderedMap().withMutations((map) => {
(collections || []).forEach(function(collection) {
map.set(collection.name, Immutable.fromJS(collection));
});
});
default:
return state;
}
}

15
src/reducers/config.js Normal file
View File

@ -0,0 +1,15 @@
import Immutable from 'immutable';
import { CONFIG } from '../actions/config';
export function config(state = null, action) {
switch (action.type) {
case CONFIG.REQUEST:
return Immutable.Map({isFetching: true});
case CONFIG.SUCCESS:
return Immutable.fromJS(action.payload);
case CONFIG.FAILURE:
return Immutable.Map({error: action.payload.toString()});
default:
return state;
}
}

12
src/routes/routes.js Normal file
View File

@ -0,0 +1,12 @@
import React from 'react';
import { Router, Route, IndexRoute, browserHistory } from 'react-router';
import App from '../containers/App';
import DashboardPage from '../containers/DashboardPage';
export default () => (
<Router history={browserHistory}>
<Route path="/" component={App}>
<IndexRoute component={DashboardPage}/>
</Route>
</Router>
);

View File

@ -0,0 +1,21 @@
import { createStore, applyMiddleware, combineReducers, compose } from 'redux';
import thunkMiddleware from 'redux-thunk';
import { browserHistory } from 'react-router';
import { syncHistory, routeReducer } from 'react-router-redux';
import { config } from '../reducers/config';
import { collections } from '../reducers/collections';
const reducer = combineReducers({
config,
collections,
router: routeReducer
});
const createStoreWithMiddleware = compose(
applyMiddleware(thunkMiddleware, syncHistory(browserHistory)),
window.devToolsExtension ? window.devToolsExtension() : (f) => f
)(createStore);
export default (initialState) => (
createStoreWithMiddleware(reducer, initialState)
);