Skip to content
This repository was archived by the owner on Mar 5, 2019. It is now read-only.
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions src/js/frontend/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
"dependencies": {
"@n3dst4/browser-bundle": "^1.1.0",
"@n3dst4/build-stylesheets": "^1.0.1",
"axios": "^0.15.3",
"babel-polyfill": "6.9.1",
"browser-sync": "^2.12.3",
"d3": "^4.3.0",
Expand All @@ -38,7 +39,9 @@
"react-redux": "^4.4.5",
"react-router": "^2.4.1",
"redux": "^3.6.0",
"redux-devtools-extension": "^2.13.0",
"redux-logger": "^2.6.1",
"redux-promise-middleware": "^4.2.0",
"redux-storage": "^4.0.1",
"redux-storage-decorator-debounce": "^1.0.1",
"redux-storage-engine-localstorage": "^1.1.1",
Expand Down
6 changes: 6 additions & 0 deletions src/js/frontend/src/Redux/action-types.js
Original file line number Diff line number Diff line change
@@ -1,2 +1,8 @@
export const TOGGLE_ABOUT = "TOGGLE_ABOUT"

export const ADD_ENTITY = "ADD_ENTITY"

export const FETCH_ENTITIES = "FETCH_ENTITIES"
export const FETCH_ENTITIES_PENDING = "FETCH_ENTITIES_PENDING"
export const FETCH_ENTITIES_FULFILLED = "FETCH_ENTITIES_FULFILLED"
export const FETCH_ENTITIES_REJECTED = "FETCH_ENTITIES_REJECTED"
15 changes: 15 additions & 0 deletions src/js/frontend/src/Redux/actions.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import * as actionTypes from "./action-types"
import entityData from "../lib/entityData"

// These are action creators
// http://redux.js.org/docs/basics/Actions.html#action-creators
Expand All @@ -17,3 +18,17 @@ export const addEntity = (entityType, id, data) => {
data,
}
}

export const fetchEntities = (entityType) => {
const request = entityData.fetchEntitiesRequest(entityType);

// When dispatched, redux-promise-middleware will handle this and
// magically dispatch FETCH_ENTITIES_{PENDING,FULFILLED,REJECTED}
// actions according to the 3 possible stages of the conversation
// with the API endpoint.
return {
type: actionTypes.FETCH_ENTITIES,
meta: { entityType },
payload: request
}
}
22 changes: 14 additions & 8 deletions src/js/frontend/src/Redux/store.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,13 @@ import {rootReducer, storeStructure, initialStoreState} from "./reducer"
import * as storage from "redux-storage"
import createEngine from "redux-storage-engine-localstorage"
import storageDebounce from 'redux-storage-decorator-debounce'
import promiseMiddleware from 'redux-promise-middleware'
import immutablejs from 'redux-storage-decorator-immutablejs'
import merger from 'redux-storage-merger-immutablejs'

////////////////////////////////////////////////////////////////////////////////
// redux stuff
import { composeWithDevTools } from 'redux-devtools-extension'

// wrap our main reducer in a storage reducer - this intercepts LOAD actions and
// calls the merger function to merge in the new state
Expand All @@ -31,18 +33,22 @@ const loggerMiddleware = createLogger({
//predicate: (getState, action) => action.type !== CALCULATION_NEEDS_REFRESH
})

// now create our redux store, applying all our middleware
// const store = createStore(reducer, applyMiddleware(
// thunkMiddleware, // first, so function results get transformed
// loggerMiddleware, // now log everything at this state
// storageMiddleware // finally the storage middleware
// ))

const composeEnhancers = composeWithDevTools({
name: 'MUTI frontend',
// actionsBlacklist: ['REDUX_STORAGE_SAVE']
});

const store = createStore(
reducer,
initialStoreState,
window.__REDUX_DEVTOOLS_EXTENSION__ && window.__REDUX_DEVTOOLS_EXTENSION__()
composeEnhancers(
applyMiddleware(
thunkMiddleware, // first, so function results get transformed
promiseMiddleware(),
// loggerMiddleware, // now log everything at this state
// storageMiddleware, // finally the storage middleware
)
)
);

window.store = store;
Expand Down
17 changes: 17 additions & 0 deletions src/js/frontend/src/components/Home.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,23 @@ class OrgList extends React.Component {
</Link>
</li>
</ul>
<ul>
<li>
<Link to="/organisations">
See all organisations
</Link>
</li>
<li>
<Link to="/government-offices">
See all government offices
</Link>
</li>
<li>
<Link to="/people">
See all people
</Link>
</li>
</ul>
<p>
Here are sample URLs you can use to visualize parts of the
data set relating to a given organisation. They assume that
Expand Down
44 changes: 44 additions & 0 deletions src/js/frontend/src/containers/EntityListContainer.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
import React from "react"
import Radium from "radium"
import { connect } from 'react-redux';

import * as actions from "../Redux/actions"
import entityData from "../lib/entityData"

class EntityListContainer extends React.Component {
componentWillMount () {
this.props.fetchEntities(this.props.route.entityType);
}

render () {
let entityType = this.props.route.entityType;

if (! entityData.isValidType(entityType)) {
return <div className="error">
Invalid entity type {entityType}.
Must be one of: {entityData.validTypes.join(", ")}
</div>
}

return <div className="entity-list">
List of {entityType} entities
</div>
}
}

// const mapStateToProps = (state) => {
// return {
// entities: state.get("entities"),
// };
// };

const mapDispatchToProps = (dispatch) => {
return {
fetchEntities: (entityType) => {
dispatch(actions.fetchEntities(entityType));
}
}
}

export default connect(null, mapDispatchToProps)(Radium(EntityListContainer));

4 changes: 4 additions & 0 deletions src/js/frontend/src/lib/api.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,10 @@ let api = {
URL: function (path) {
return this.protocol + "://" + this.host + ":" + this.port +
this.apiPathPrefix + path;
},

fetchEntitiesURL: function (entityType) {
return this.URL(entityType);
}
}

Expand Down
27 changes: 23 additions & 4 deletions src/js/frontend/src/lib/entityData.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,21 @@
let d3 = require('d3');
let api = require('./api');
import axios from "axios"

let entityData = {
apiURL: function(entityType, entityId) {
return api.URL(entityType + "/" + entityId);
},

validTypes: [
"organisations", "people", "government-offices"
],

isValidType: function(entityType) {
return this.validTypes.indexOf(entityType) >= 0;
},

// FIXME: retire this in favour of fetchEntitiesRequest() below
fetch: function(entityType, entityId, onSuccess) {
let url = this.apiURL(entityType, entityId);
console.debug("Fetching from " + url);
Expand All @@ -12,10 +26,6 @@ let entityData = {
.get(this.dataLoadedHandler(onSuccess, url, entityType, entityId));
},

apiURL: function(entityType, entityId) {
return api.URL(entityType + "/" + entityId);
},

dataLoadedHandler: function(onSuccess, url, entityType, entityId) {
let self = this;

Expand All @@ -39,6 +49,15 @@ let entityData = {
onSuccess(json);
};
},

fetchEntitiesRequest: function (entityType) {
return axios.get(api.fetchEntitiesURL(entityType), {
headers: {
"Content-Type": "application/json",
"Accept": "application/vnd.api+json"
},
});
}
}

module.exports = entityData;
7 changes: 7 additions & 0 deletions src/js/frontend/src/main.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import App from "./app"
import PageLayout from "./containers/PageLayout"
import Home from "./components/Home"
import ChartContainer from "./containers/ChartContainer"
import EntityListContainer from "./containers/EntityListContainer"

// now everything is set up, create a loader and use it to load the store
const load = storage.createLoader(engine)
Expand All @@ -25,6 +26,12 @@ loaded.then((newState) => {
<IndexRoute component={Home} />
<Route path="demo"
component={ChartContainer} sourceType="demo" />
<Route path="organisations"
component={EntityListContainer} entityType="organisations" />
<Route path="government-offices"
component={EntityListContainer} entityType="government-offices" />
<Route path="people"
component={EntityListContainer} entityType="people" />
<Route path="organisation/:id/:targetType"
component={ChartContainer} sourceType="organisations" />
<Route path="government-office/:id/:targetType"
Expand Down