From 8df749ff57926fe5505fb41cf57136146882a4d4 Mon Sep 17 00:00:00 2001 From: Alessandro DiMarco Date: Sat, 14 Jan 2017 14:35:42 -0500 Subject: [PATCH 01/15] Create TurkHit component --- src/components/TurkHit/defaultForm.js | 16 ++++++++++++++++ src/components/TurkHit/index.js | 22 ++++++++++++++++++++++ src/components/TurkHit/styles.css | 0 3 files changed, 38 insertions(+) create mode 100644 src/components/TurkHit/defaultForm.js create mode 100644 src/components/TurkHit/index.js create mode 100644 src/components/TurkHit/styles.css diff --git a/src/components/TurkHit/defaultForm.js b/src/components/TurkHit/defaultForm.js new file mode 100644 index 00000000..b8fa47c8 --- /dev/null +++ b/src/components/TurkHit/defaultForm.js @@ -0,0 +1,16 @@ +// blocpower_id and address will be added from component +const defaultTurkHit = { + instructions_text: 'We need to get building dimensions for buildings using Google Earth. Read the instructions below on what measurements need to be taken.', + instructions_url: 'http://beta.blocpower.us/dimensions/BuildingDimensionsInstructions.pdf', + worksheet_url: 'http://beta.blocpower.us/dimensions/BuildingDimensionsTemplate.xlsx', + max_assignments: '1', + title: 'Measure building dimensions', + description: 'Use google earth to measure building\'s dimensions, windows, and doors', + keywords: 'building, dimensions, google earth', + duration: '300', + reward: '5', + MIN_FILE_BYTES: 1, + MAX_FILE_BYTES: 10, +}; + +export default defaultTurkHit; diff --git a/src/components/TurkHit/index.js b/src/components/TurkHit/index.js new file mode 100644 index 00000000..2111d479 --- /dev/null +++ b/src/components/TurkHit/index.js @@ -0,0 +1,22 @@ +import React, { PropTypes } from 'react'; +import defaultForm from './defaultForm'; +import './styles.css'; + +export default function TurkHit({ createHit, address, blocpower_id }) { + return ( +
+ +
+ ); +} + +TurkHit.propTypes = { + createHit: PropTypes.func, + address: PropTypes.string, + blocpower_id: PropTypes.number, +}; diff --git a/src/components/TurkHit/styles.css b/src/components/TurkHit/styles.css new file mode 100644 index 00000000..e69de29b -- GitLab From 18bc3c58ec27a5f4a9c5ff3fe9860db5eb9bb05a Mon Sep 17 00:00:00 2001 From: Alessandro DiMarco Date: Sat, 14 Jan 2017 15:14:31 -0500 Subject: [PATCH 02/15] Add CREATE_HIT action --- src/containers/BuildingDetail/actions.js | 37 ++++++++++++++-------- src/containers/BuildingDetail/constants.js | 3 +- src/containers/BuildingDetail/reducer.js | 13 ++++++-- 3 files changed, 35 insertions(+), 18 deletions(-) diff --git a/src/containers/BuildingDetail/actions.js b/src/containers/BuildingDetail/actions.js index e31836f2..463828a3 100644 --- a/src/containers/BuildingDetail/actions.js +++ b/src/containers/BuildingDetail/actions.js @@ -1,25 +1,34 @@ import 'whatwg-fetch'; -import { FETCH_BUILDING_DETAIL } from './constants'; +import { FETCH_BUILDING_DETAIL, CREATE_HIT } from './constants'; -const ROOT_URL = `${process.env.REACT_APP_BUILDING_SERVICE}/building/`; +const ROOT_URL = `${process.env.REACT_APP_BUILDING_SERVICE}`; const HEADERS = new Headers({ 'x-blocpower-app-key': process.env.REACT_APP_KEY }); -const init = { - method: 'GET', - headers: HEADERS, - mode: 'cors', - cache: 'default', -}; - function fetchBuildingDetail(blocPowerID) { return { type: FETCH_BUILDING_DETAIL, - payload: fetch(`${ROOT_URL}${blocPowerID}`, init).then(response => - response.json() + payload: fetch(`${ROOT_URL}/building/${blocPowerID}`, + { + method: 'GET', + headers: HEADERS, + }).then(response => + response.json() + ), + }; +} + +function createHit(data) { + return { + type: CREATE_HIT, + payload: fetch(`${ROOT_URL}/turkhit/`, + { + method: 'POST', + headers: HEADERS, + body: JSON.stringify(data), + }).then(response => + response.json() ), }; } -/* eslint-disable import/prefer-default-export */ -export { fetchBuildingDetail }; -/* eslint-enable */ +export { fetchBuildingDetail, createHit }; diff --git a/src/containers/BuildingDetail/constants.js b/src/containers/BuildingDetail/constants.js index 1d22cced..7aee1a08 100644 --- a/src/containers/BuildingDetail/constants.js +++ b/src/containers/BuildingDetail/constants.js @@ -1,3 +1,2 @@ -/* eslint-disable import/prefer-default-export */ export const FETCH_BUILDING_DETAIL = 'FETCH_BUILDING_DETAIL'; -/* eslint-enable */ +export const CREATE_HIT = 'CREATE_HIT'; diff --git a/src/containers/BuildingDetail/reducer.js b/src/containers/BuildingDetail/reducer.js index a49aa9c3..a5382604 100644 --- a/src/containers/BuildingDetail/reducer.js +++ b/src/containers/BuildingDetail/reducer.js @@ -1,9 +1,18 @@ -import { FETCH_BUILDING_DETAIL } from './constants'; +import { FETCH_BUILDING_DETAIL, CREATE_HIT } from './constants'; export default function (state = {}, action) { switch (action.type) { case FETCH_BUILDING_DETAIL: - return action.payload.data; + return { ...state, overview: action.payload.data }; + + case CREATE_HIT: + return { + ...state, + hit: { + id: action.payload.data.hit_id, + status: 'Submitted', + }, + }; default: return state; -- GitLab From 2ccd5b5040ac1672cfc106767b4ae73239f58d20 Mon Sep 17 00:00:00 2001 From: Alessandro DiMarco Date: Sat, 14 Jan 2017 15:16:17 -0500 Subject: [PATCH 03/15] Add TurkHit component to BuildingDetail --- src/components/BuildingOverview/index.js | 5 --- src/containers/BuildingDetail/index.js | 39 +++++++++++++++++++----- 2 files changed, 31 insertions(+), 13 deletions(-) diff --git a/src/components/BuildingOverview/index.js b/src/components/BuildingOverview/index.js index f07ec448..d335ab6c 100644 --- a/src/components/BuildingOverview/index.js +++ b/src/components/BuildingOverview/index.js @@ -1,11 +1,6 @@ import React, { PropTypes } from 'react'; export default function BuildingOverview({ building }) { - if (Object.keys(building).length === 0) { - // TODO add loading icon? - return
...
; - } - return (

diff --git a/src/containers/BuildingDetail/index.js b/src/containers/BuildingDetail/index.js index 1339a137..2143a3fc 100644 --- a/src/containers/BuildingDetail/index.js +++ b/src/containers/BuildingDetail/index.js @@ -1,9 +1,11 @@ import React, { Component, PropTypes } from 'react'; import { connect } from 'react-redux'; +import { bindActionCreators } from 'redux'; -import { fetchBuildingDetail } from './actions'; +import { fetchBuildingDetail, createHit } from './actions'; import './styles.css'; import BuildingOverview from '../../components/BuildingOverview'; +import TurkHit from '../../components/TurkHit'; class BuildingDetail extends Component { componentDidMount() { @@ -11,24 +13,45 @@ class BuildingDetail extends Component { } render() { - return ; + if (!this.props.buildingDetail.overview) { + // TODO fix return + return
...
; + } + + return ( +
+ + +
+ ); } } BuildingDetail.propTypes = { buildingDetail: PropTypes.shape({ - address: PropTypes.string, - bbl: PropTypes.number, - blocpower_id: PropTypes.number, - borough: PropTypes.string, - zipcode: PropTypes.number, + overview: PropTypes.shape({ + address: PropTypes.string, + bbl: PropTypes.number, + blocpower_id: PropTypes.number, + borough: PropTypes.string, + zipcode: PropTypes.number, + }), }), params: PropTypes.objectOf(PropTypes.string), fetchBuildingDetail: PropTypes.func, + createHit: PropTypes.func, }; +function mapDispatchToProps(dispatch) { + return bindActionCreators({ fetchBuildingDetail, createHit }, dispatch); +} + function mapStateToProps({ buildingDetail }) { return { buildingDetail }; } -export default connect(mapStateToProps, { fetchBuildingDetail })(BuildingDetail); +export default connect(mapStateToProps, mapDispatchToProps)(BuildingDetail); -- GitLab From 109fbf17ef6f440f936ebd022e9aebfea4f95127 Mon Sep 17 00:00:00 2001 From: Alessandro DiMarco Date: Sat, 14 Jan 2017 16:07:27 -0500 Subject: [PATCH 04/15] Display Hit status and id --- src/components/TurkHit/index.js | 17 ++++++++++++++++- src/containers/BuildingDetail/index.js | 10 +++++----- src/containers/BuildingDetail/reducer.js | 16 +++++++++++++++- 3 files changed, 36 insertions(+), 7 deletions(-) diff --git a/src/components/TurkHit/index.js b/src/components/TurkHit/index.js index 2111d479..fa0af85b 100644 --- a/src/components/TurkHit/index.js +++ b/src/components/TurkHit/index.js @@ -2,7 +2,17 @@ import React, { PropTypes } from 'react'; import defaultForm from './defaultForm'; import './styles.css'; -export default function TurkHit({ createHit, address, blocpower_id }) { +export default function TurkHit({ createHit, address, blocpower_id, hit }) { + let hitStatus =
; + if (hit.status !== '') { + hitStatus = ( +
+

HIT Id: {hit.id}

+

HIT Status: {hit.status}

+
+ ); + } + return (
+ {hitStatus}
); } @@ -19,4 +30,8 @@ TurkHit.propTypes = { createHit: PropTypes.func, address: PropTypes.string, blocpower_id: PropTypes.number, + hit: PropTypes.shape({ + id: PropTypes.string, + status: PropTypes.string, + }), }; diff --git a/src/containers/BuildingDetail/index.js b/src/containers/BuildingDetail/index.js index 2143a3fc..c4e9fb9a 100644 --- a/src/containers/BuildingDetail/index.js +++ b/src/containers/BuildingDetail/index.js @@ -13,11 +13,6 @@ class BuildingDetail extends Component { } render() { - if (!this.props.buildingDetail.overview) { - // TODO fix return - return
...
; - } - return (
@@ -25,6 +20,7 @@ class BuildingDetail extends Component { createHit={this.props.createHit} address={this.props.buildingDetail.overview.address} blocpower_id={this.props.buildingDetail.overview.blocpower_id} + hit={this.props.buildingDetail.hit} />
); @@ -40,6 +36,10 @@ BuildingDetail.propTypes = { borough: PropTypes.string, zipcode: PropTypes.number, }), + hit: PropTypes.shape({ + id: PropTypes.string, + status: PropTypes.string, + }), }), params: PropTypes.objectOf(PropTypes.string), fetchBuildingDetail: PropTypes.func, diff --git a/src/containers/BuildingDetail/reducer.js b/src/containers/BuildingDetail/reducer.js index a5382604..44b66bf7 100644 --- a/src/containers/BuildingDetail/reducer.js +++ b/src/containers/BuildingDetail/reducer.js @@ -1,6 +1,20 @@ import { FETCH_BUILDING_DETAIL, CREATE_HIT } from './constants'; -export default function (state = {}, action) { +const initState = { + overview: { + address: '', + bbl: 0, + blocpower_id: 0, + borough: '', + zipcode: 0, + }, + hit: { + id: '', + status: '', + }, +}; + +export default function (state = initState, action) { switch (action.type) { case FETCH_BUILDING_DETAIL: return { ...state, overview: action.payload.data }; -- GitLab From 492aba15da942a7784dde1934a6869d293b27735 Mon Sep 17 00:00:00 2001 From: Alessandro DiMarco Date: Mon, 16 Jan 2017 13:25:01 -0500 Subject: [PATCH 05/15] Add turkhit saga --- package.json | 5 ++- src/containers/BuildingDetail/actions.js | 8 +--- src/containers/BuildingDetail/constants.js | 1 + src/containers/BuildingDetail/reducer.js | 6 +-- src/containers/BuildingDetail/sagas.js | 26 +++++++++++++ src/store.js | 10 ++++- src/utils/request.js | 45 ++++++++++++++++++++++ 7 files changed, 87 insertions(+), 14 deletions(-) create mode 100644 src/containers/BuildingDetail/sagas.js create mode 100644 src/utils/request.js diff --git a/package.json b/package.json index e8bd9fea..d3a3171d 100644 --- a/package.json +++ b/package.json @@ -55,6 +55,7 @@ "whatwg-fetch": "1.0.0" }, "dependencies": { + "bpl": "git+https://d0516feb598c9e6cbc54d20cf52b7b41de2d7a3d:x-oauth-basic@github.com/Blocp/bpl.git", "react": "^15.3.2", "react-dom": "^15.3.2", "react-redux": "^4.4.5", @@ -62,8 +63,8 @@ "react-router-redux": "^4.0.7", "redux": "^3.6.0", "redux-promise": "^0.5.3", - "whatwg-fetch": "^1.0.0", - "bpl": "git+https://d0516feb598c9e6cbc54d20cf52b7b41de2d7a3d:x-oauth-basic@github.com/Blocp/bpl.git" + "redux-saga": "^0.14.2", + "whatwg-fetch": "^1.0.0" }, "scripts": { "start": "node scripts/start.js", diff --git a/src/containers/BuildingDetail/actions.js b/src/containers/BuildingDetail/actions.js index 463828a3..35eb3ad1 100644 --- a/src/containers/BuildingDetail/actions.js +++ b/src/containers/BuildingDetail/actions.js @@ -7,13 +7,7 @@ const HEADERS = new Headers({ 'x-blocpower-app-key': process.env.REACT_APP_KEY } function fetchBuildingDetail(blocPowerID) { return { type: FETCH_BUILDING_DETAIL, - payload: fetch(`${ROOT_URL}/building/${blocPowerID}`, - { - method: 'GET', - headers: HEADERS, - }).then(response => - response.json() - ), + payload: blocPowerID, }; } diff --git a/src/containers/BuildingDetail/constants.js b/src/containers/BuildingDetail/constants.js index 7aee1a08..dc9a25c0 100644 --- a/src/containers/BuildingDetail/constants.js +++ b/src/containers/BuildingDetail/constants.js @@ -1,2 +1,3 @@ export const FETCH_BUILDING_DETAIL = 'FETCH_BUILDING_DETAIL'; +export const FETCH_BUILDING_DETAIL_SUCCEEDED = 'FETCH_BUILDING_DETAIL_SUCCEEDED'; export const CREATE_HIT = 'CREATE_HIT'; diff --git a/src/containers/BuildingDetail/reducer.js b/src/containers/BuildingDetail/reducer.js index 44b66bf7..69cb8013 100644 --- a/src/containers/BuildingDetail/reducer.js +++ b/src/containers/BuildingDetail/reducer.js @@ -1,4 +1,4 @@ -import { FETCH_BUILDING_DETAIL, CREATE_HIT } from './constants'; +import { FETCH_BUILDING_DETAIL_SUCCEEDED, CREATE_HIT } from './constants'; const initState = { overview: { @@ -16,8 +16,8 @@ const initState = { export default function (state = initState, action) { switch (action.type) { - case FETCH_BUILDING_DETAIL: - return { ...state, overview: action.payload.data }; + case FETCH_BUILDING_DETAIL_SUCCEEDED: + return { ...state, overview: action.payload.data.building }; case CREATE_HIT: return { diff --git a/src/containers/BuildingDetail/sagas.js b/src/containers/BuildingDetail/sagas.js new file mode 100644 index 00000000..fa1b671a --- /dev/null +++ b/src/containers/BuildingDetail/sagas.js @@ -0,0 +1,26 @@ +import { call, put, takeEvery } from 'redux-saga/effects'; +import { FETCH_BUILDING_DETAIL, FETCH_BUILDING_DETAIL_SUCCEEDED } from './constants'; +import request from '../../utils/request'; + +const ROOT_URL = `${process.env.REACT_APP_BUILDING_SERVICE}`; +const HEADERS = new Headers({ 'x-blocpower-app-key': process.env.REACT_APP_KEY }); + +function* fetchDetailPage(action) { + const data = yield call(request, `${ROOT_URL}/building/${action.payload}`, { + method: 'GET', + headers: HEADERS, + }); + + if (!data.err) { + yield put({ type: FETCH_BUILDING_DETAIL_SUCCEEDED, payload: data }); + } else { + // TODO handle error instead + console.error(data.err); + } +} + +function* mySaga() { + yield takeEvery(FETCH_BUILDING_DETAIL, fetchDetailPage); +} + +export default mySaga; diff --git a/src/store.js b/src/store.js index d397ae73..c6ff1265 100644 --- a/src/store.js +++ b/src/store.js @@ -2,7 +2,12 @@ import { createStore, applyMiddleware, compose } from 'redux'; import ReduxPromise from 'redux-promise'; import { browserHistory } from 'react-router'; import { syncHistoryWithStore } from 'react-router-redux'; +import createSagaMiddleware from 'redux-saga'; + import rootReducer from './reducer'; +import buildingDetailSaga from './containers/BuildingDetail/sagas'; + +const sagaMiddleware = createSagaMiddleware(); /* eslint-disable no-underscore-dangle */ const composeEnhancers = @@ -14,13 +19,14 @@ const composeEnhancers = }) : compose; const enhancer = composeEnhancers( - applyMiddleware(ReduxPromise), + applyMiddleware(sagaMiddleware, ReduxPromise), // other store enhancers if any ); /* eslint-enable */ const store = createStore(rootReducer, enhancer); - const history = syncHistoryWithStore(browserHistory, store); +sagaMiddleware.run(buildingDetailSaga); + export { store, history }; diff --git a/src/utils/request.js b/src/utils/request.js new file mode 100644 index 00000000..958fb3fc --- /dev/null +++ b/src/utils/request.js @@ -0,0 +1,45 @@ +import 'whatwg-fetch'; + +/** + * Parses the JSON returned by a network request + * + * @param {object} response A response from a network request + * + * @return {object} The parsed JSON from the request + */ +function parseJSON(response) { + return response.json(); +} + +/** + * Checks if a network request came back fine, and throws an error if not + * + * @param {object} response A response from a network request + * + * @return {object|undefined} Returns either the response, or throws an error + */ +function checkStatus(response) { + if (response.status >= 200 && response.status < 300) { + return response; + } + + const error = new Error(response.statusText); + error.response = response; + throw error; +} + +/** + * Requests a URL, returning a promise + * + * @param {string} url The URL we want to request + * @param {object} [options] The options we want to pass to "fetch" + * + * @return {object} An object containing either "data" or "err" + */ +export default function request(url, options) { + return fetch(url, options) + .then(checkStatus) + .then(parseJSON) + .then(data => ({ data })) + .catch(err => ({ err })); +} -- GitLab From 3d3e43afc56e949275895ffc6df56c05e1ff825f Mon Sep 17 00:00:00 2001 From: Alessandro DiMarco Date: Mon, 16 Jan 2017 14:18:04 -0500 Subject: [PATCH 06/15] Remove building overview from initital state --- src/containers/BuildingDetail/index.js | 21 ++++++++++++++------- src/containers/BuildingDetail/reducer.js | 7 ------- 2 files changed, 14 insertions(+), 14 deletions(-) diff --git a/src/containers/BuildingDetail/index.js b/src/containers/BuildingDetail/index.js index c4e9fb9a..4972015b 100644 --- a/src/containers/BuildingDetail/index.js +++ b/src/containers/BuildingDetail/index.js @@ -13,15 +13,22 @@ class BuildingDetail extends Component { } render() { + let overview =
; + let hit =
; + if (this.props.buildingDetail.overview) { + overview = ; + hit = (); + } + return (
- - + {overview} + {hit}
); } diff --git a/src/containers/BuildingDetail/reducer.js b/src/containers/BuildingDetail/reducer.js index 69cb8013..91df909a 100644 --- a/src/containers/BuildingDetail/reducer.js +++ b/src/containers/BuildingDetail/reducer.js @@ -1,13 +1,6 @@ import { FETCH_BUILDING_DETAIL_SUCCEEDED, CREATE_HIT } from './constants'; const initState = { - overview: { - address: '', - bbl: 0, - blocpower_id: 0, - borough: '', - zipcode: 0, - }, hit: { id: '', status: '', -- GitLab From f6fe2f65be0b19023b97fce4c096d3169f0be622 Mon Sep 17 00:00:00 2001 From: Alessandro DiMarco Date: Mon, 16 Jan 2017 14:32:55 -0500 Subject: [PATCH 07/15] Rename rootReducer file to reducers.js --- src/{reducer.js => reducers.js} | 0 src/store.js | 2 +- 2 files changed, 1 insertion(+), 1 deletion(-) rename src/{reducer.js => reducers.js} (100%) diff --git a/src/reducer.js b/src/reducers.js similarity index 100% rename from src/reducer.js rename to src/reducers.js diff --git a/src/store.js b/src/store.js index c6ff1265..569cbca0 100644 --- a/src/store.js +++ b/src/store.js @@ -4,7 +4,7 @@ import { browserHistory } from 'react-router'; import { syncHistoryWithStore } from 'react-router-redux'; import createSagaMiddleware from 'redux-saga'; -import rootReducer from './reducer'; +import rootReducer from './reducers'; import buildingDetailSaga from './containers/BuildingDetail/sagas'; const sagaMiddleware = createSagaMiddleware(); -- GitLab From ea32d7bbbcdeccf278e5e5d16cc40961d3372ad1 Mon Sep 17 00:00:00 2001 From: Alessandro DiMarco Date: Mon, 16 Jan 2017 14:44:35 -0500 Subject: [PATCH 08/15] Change css file extensions to scss --- src/components/BuildingListTable/index.js | 2 +- src/components/BuildingListTable/{styles.css => styles.scss} | 0 src/components/BuildingOverview/index.js | 1 + src/components/BuildingOverview/{styles.css => styles.scss} | 0 src/components/TurkHit/index.js | 2 +- src/components/TurkHit/{styles.css => styles.scss} | 0 src/containers/BuildingDetail/index.js | 2 +- src/containers/BuildingDetail/{styles.css => styles.scss} | 0 src/containers/BuildingList/index.js | 2 +- src/containers/BuildingList/{styles.css => styles.scss} | 0 10 files changed, 5 insertions(+), 4 deletions(-) rename src/components/BuildingListTable/{styles.css => styles.scss} (100%) rename src/components/BuildingOverview/{styles.css => styles.scss} (100%) rename src/components/TurkHit/{styles.css => styles.scss} (100%) rename src/containers/BuildingDetail/{styles.css => styles.scss} (100%) rename src/containers/BuildingList/{styles.css => styles.scss} (100%) diff --git a/src/components/BuildingListTable/index.js b/src/components/BuildingListTable/index.js index 8e2adfc4..275586a5 100644 --- a/src/components/BuildingListTable/index.js +++ b/src/components/BuildingListTable/index.js @@ -1,6 +1,6 @@ import React, { PropTypes } from 'react'; import { Link } from 'react-router'; -import './styles.css'; +import './styles.scss'; export default function BuildingListTable({ buildings }) { if (!buildings || buildings.length === 0) { diff --git a/src/components/BuildingListTable/styles.css b/src/components/BuildingListTable/styles.scss similarity index 100% rename from src/components/BuildingListTable/styles.css rename to src/components/BuildingListTable/styles.scss diff --git a/src/components/BuildingOverview/index.js b/src/components/BuildingOverview/index.js index d335ab6c..ee12d28f 100644 --- a/src/components/BuildingOverview/index.js +++ b/src/components/BuildingOverview/index.js @@ -1,4 +1,5 @@ import React, { PropTypes } from 'react'; +import './styles.scss'; export default function BuildingOverview({ building }) { return ( diff --git a/src/components/BuildingOverview/styles.css b/src/components/BuildingOverview/styles.scss similarity index 100% rename from src/components/BuildingOverview/styles.css rename to src/components/BuildingOverview/styles.scss diff --git a/src/components/TurkHit/index.js b/src/components/TurkHit/index.js index fa0af85b..bf9819fe 100644 --- a/src/components/TurkHit/index.js +++ b/src/components/TurkHit/index.js @@ -1,6 +1,6 @@ import React, { PropTypes } from 'react'; import defaultForm from './defaultForm'; -import './styles.css'; +import './styles.scss'; export default function TurkHit({ createHit, address, blocpower_id, hit }) { let hitStatus =
; diff --git a/src/components/TurkHit/styles.css b/src/components/TurkHit/styles.scss similarity index 100% rename from src/components/TurkHit/styles.css rename to src/components/TurkHit/styles.scss diff --git a/src/containers/BuildingDetail/index.js b/src/containers/BuildingDetail/index.js index 4972015b..c5b070a8 100644 --- a/src/containers/BuildingDetail/index.js +++ b/src/containers/BuildingDetail/index.js @@ -3,7 +3,7 @@ import { connect } from 'react-redux'; import { bindActionCreators } from 'redux'; import { fetchBuildingDetail, createHit } from './actions'; -import './styles.css'; +import './styles.scss'; import BuildingOverview from '../../components/BuildingOverview'; import TurkHit from '../../components/TurkHit'; diff --git a/src/containers/BuildingDetail/styles.css b/src/containers/BuildingDetail/styles.scss similarity index 100% rename from src/containers/BuildingDetail/styles.css rename to src/containers/BuildingDetail/styles.scss diff --git a/src/containers/BuildingList/index.js b/src/containers/BuildingList/index.js index a06bf3c1..90de57dc 100644 --- a/src/containers/BuildingList/index.js +++ b/src/containers/BuildingList/index.js @@ -5,7 +5,7 @@ import { bindActionCreators } from 'redux'; import { fetchBuildings, searchTerm } from './actions'; import BuildingListTable from '../../components/BuildingListTable'; -import './styles.css'; +import './styles.scss'; import { SearchSVG } from '../../components/bpl'; // TODO remove this address diff --git a/src/containers/BuildingList/styles.css b/src/containers/BuildingList/styles.scss similarity index 100% rename from src/containers/BuildingList/styles.css rename to src/containers/BuildingList/styles.scss -- GitLab From a04353c42d32d68fa8edee7e2cf0d4d0d99362e0 Mon Sep 17 00:00:00 2001 From: Alessandro DiMarco Date: Mon, 16 Jan 2017 17:33:55 -0500 Subject: [PATCH 09/15] Add sidebar component and dynamically load main content in route --- src/components/SideBarDetail/index.js | 10 ++++++++ src/components/SideBarDetail/styles.scss | 0 src/containers/BuildingDetail/index.js | 30 +++++++++++++++++++----- src/routes.js | 7 ++++-- 4 files changed, 39 insertions(+), 8 deletions(-) create mode 100644 src/components/SideBarDetail/index.js create mode 100644 src/components/SideBarDetail/styles.scss diff --git a/src/components/SideBarDetail/index.js b/src/components/SideBarDetail/index.js new file mode 100644 index 00000000..fb62883e --- /dev/null +++ b/src/components/SideBarDetail/index.js @@ -0,0 +1,10 @@ +import React from 'react'; +import './styles.scss'; + +export default function SideBarDetail() { + return ( +
+

Sidebar

+
+ ); +} diff --git a/src/components/SideBarDetail/styles.scss b/src/components/SideBarDetail/styles.scss new file mode 100644 index 00000000..e69de29b diff --git a/src/containers/BuildingDetail/index.js b/src/containers/BuildingDetail/index.js index c5b070a8..f0a53f2f 100644 --- a/src/containers/BuildingDetail/index.js +++ b/src/containers/BuildingDetail/index.js @@ -4,19 +4,31 @@ import { bindActionCreators } from 'redux'; import { fetchBuildingDetail, createHit } from './actions'; import './styles.scss'; -import BuildingOverview from '../../components/BuildingOverview'; +import SideBarDetail from '../../components/SideBarDetail'; import TurkHit from '../../components/TurkHit'; + class BuildingDetail extends Component { componentDidMount() { this.props.fetchBuildingDetail(this.props.params.blocPowerID); } render() { - let overview =
; + let mainContent =
; let hit =
; + if (this.props.buildingDetail.overview) { - overview = ; + switch (this.props.main.type.name) { + case 'BuildingOverview': + mainContent = React.cloneElement(this.props.main, { + building: this.props.buildingDetail.overview, + }); + break; + + default: + return
; + } + hit = ( - {overview} - {hit} +
+
+ +
+
+ {mainContent} + {hit} +
); } @@ -51,6 +68,7 @@ BuildingDetail.propTypes = { params: PropTypes.objectOf(PropTypes.string), fetchBuildingDetail: PropTypes.func, createHit: PropTypes.func, + main: React.PropTypes.element, }; function mapDispatchToProps(dispatch) { diff --git a/src/routes.js b/src/routes.js index 281d3477..561a2e0e 100644 --- a/src/routes.js +++ b/src/routes.js @@ -2,13 +2,16 @@ import React from 'react'; import { Route, IndexRoute, IndexRedirect } from 'react-router'; import BuildingList from './containers/BuildingList'; import BuildingDetail from './containers/BuildingDetail'; +import BuildingOverview from './components/BuildingOverview'; -module.exports = ( +export default ( - + + + ); -- GitLab From edbe8de353f5ab7fa401974b30c97e64c4eb8d98 Mon Sep 17 00:00:00 2001 From: Alessandro DiMarco Date: Tue, 17 Jan 2017 00:39:02 -0500 Subject: [PATCH 10/15] Add createHit watcher in sagas --- src/containers/BuildingDetail/actions.js | 14 +------ src/containers/BuildingDetail/constants.js | 1 + src/containers/BuildingDetail/reducer.js | 4 +- src/containers/BuildingDetail/sagas.js | 48 ++++++++++++++++++++-- src/sagas.js | 7 ++++ src/store.js | 4 +- 6 files changed, 58 insertions(+), 20 deletions(-) create mode 100644 src/sagas.js diff --git a/src/containers/BuildingDetail/actions.js b/src/containers/BuildingDetail/actions.js index 35eb3ad1..8eee882f 100644 --- a/src/containers/BuildingDetail/actions.js +++ b/src/containers/BuildingDetail/actions.js @@ -1,9 +1,6 @@ import 'whatwg-fetch'; import { FETCH_BUILDING_DETAIL, CREATE_HIT } from './constants'; -const ROOT_URL = `${process.env.REACT_APP_BUILDING_SERVICE}`; -const HEADERS = new Headers({ 'x-blocpower-app-key': process.env.REACT_APP_KEY }); - function fetchBuildingDetail(blocPowerID) { return { type: FETCH_BUILDING_DETAIL, @@ -11,17 +8,10 @@ function fetchBuildingDetail(blocPowerID) { }; } -function createHit(data) { +function createHit(formData) { return { type: CREATE_HIT, - payload: fetch(`${ROOT_URL}/turkhit/`, - { - method: 'POST', - headers: HEADERS, - body: JSON.stringify(data), - }).then(response => - response.json() - ), + payload: formData, }; } diff --git a/src/containers/BuildingDetail/constants.js b/src/containers/BuildingDetail/constants.js index dc9a25c0..467ee73e 100644 --- a/src/containers/BuildingDetail/constants.js +++ b/src/containers/BuildingDetail/constants.js @@ -1,3 +1,4 @@ export const FETCH_BUILDING_DETAIL = 'FETCH_BUILDING_DETAIL'; export const FETCH_BUILDING_DETAIL_SUCCEEDED = 'FETCH_BUILDING_DETAIL_SUCCEEDED'; export const CREATE_HIT = 'CREATE_HIT'; +export const CREATE_HIT_SUCCEEDED = 'CREATE_HIT_SUCCEEDED'; diff --git a/src/containers/BuildingDetail/reducer.js b/src/containers/BuildingDetail/reducer.js index 91df909a..d4272add 100644 --- a/src/containers/BuildingDetail/reducer.js +++ b/src/containers/BuildingDetail/reducer.js @@ -1,4 +1,4 @@ -import { FETCH_BUILDING_DETAIL_SUCCEEDED, CREATE_HIT } from './constants'; +import { FETCH_BUILDING_DETAIL_SUCCEEDED, CREATE_HIT_SUCCEEDED } from './constants'; const initState = { hit: { @@ -12,7 +12,7 @@ export default function (state = initState, action) { case FETCH_BUILDING_DETAIL_SUCCEEDED: return { ...state, overview: action.payload.data.building }; - case CREATE_HIT: + case CREATE_HIT_SUCCEEDED: return { ...state, hit: { diff --git a/src/containers/BuildingDetail/sagas.js b/src/containers/BuildingDetail/sagas.js index fa1b671a..947dde91 100644 --- a/src/containers/BuildingDetail/sagas.js +++ b/src/containers/BuildingDetail/sagas.js @@ -1,10 +1,18 @@ import { call, put, takeEvery } from 'redux-saga/effects'; -import { FETCH_BUILDING_DETAIL, FETCH_BUILDING_DETAIL_SUCCEEDED } from './constants'; +import { + FETCH_BUILDING_DETAIL, + FETCH_BUILDING_DETAIL_SUCCEEDED, + CREATE_HIT, + CREATE_HIT_SUCCEEDED, +} from './constants'; import request from '../../utils/request'; const ROOT_URL = `${process.env.REACT_APP_BUILDING_SERVICE}`; const HEADERS = new Headers({ 'x-blocpower-app-key': process.env.REACT_APP_KEY }); +/** + * Detail page request/response handler + */ function* fetchDetailPage(action) { const data = yield call(request, `${ROOT_URL}/building/${action.payload}`, { method: 'GET', @@ -14,13 +22,45 @@ function* fetchDetailPage(action) { if (!data.err) { yield put({ type: FETCH_BUILDING_DETAIL_SUCCEEDED, payload: data }); } else { - // TODO handle error instead + // TODO handle error properly console.error(data.err); } } -function* mySaga() { +function* createHit(action) { + const data = yield call(request, `${ROOT_URL}/turkhit/`, { + method: 'POST', + headers: HEADERS, + body: JSON.stringify(action.payload), + }); + + if (!data.err) { + yield put({ type: CREATE_HIT_SUCCEEDED, payload: data }); + } else { + // TODO handle error properly + console.error(data.err); + } +} + +/** + * Watches for FETCH_BUILDING_DETAIL & CREATE_HIT actions and calls + * the appropriate handler function. + */ +function* buildingDetailWatcher() { yield takeEvery(FETCH_BUILDING_DETAIL, fetchDetailPage); + yield takeEvery(CREATE_HIT, createHit); } -export default mySaga; +/** + * Root saga manages watcher lifecycle + */ +// export function* githubData() { + // Fork watcher so we can continue execution + // const watcher = yield fork(getReposWatcher); + + // Suspend execution until location changes + // yield take(LOCATION_CHANGE); + // yield cancel(watcher); +// } + +export default buildingDetailWatcher; diff --git a/src/sagas.js b/src/sagas.js new file mode 100644 index 00000000..644286f8 --- /dev/null +++ b/src/sagas.js @@ -0,0 +1,7 @@ +import buildingDetailSaga from './containers/BuildingDetail/sagas'; + +export default function* rootSaga() { + yield [ + buildingDetailSaga(), + ]; +} diff --git a/src/store.js b/src/store.js index 569cbca0..b6e4b001 100644 --- a/src/store.js +++ b/src/store.js @@ -5,7 +5,7 @@ import { syncHistoryWithStore } from 'react-router-redux'; import createSagaMiddleware from 'redux-saga'; import rootReducer from './reducers'; -import buildingDetailSaga from './containers/BuildingDetail/sagas'; +import rootSaga from './sagas'; const sagaMiddleware = createSagaMiddleware(); @@ -27,6 +27,6 @@ const enhancer = composeEnhancers( const store = createStore(rootReducer, enhancer); const history = syncHistoryWithStore(browserHistory, store); -sagaMiddleware.run(buildingDetailSaga); +sagaMiddleware.run(rootSaga); export { store, history }; -- GitLab From e74a82c7eb09c36aa6d0fdce5ff1d6744869718b Mon Sep 17 00:00:00 2001 From: Alessandro DiMarco Date: Tue, 17 Jan 2017 02:33:05 -0500 Subject: [PATCH 11/15] Add turkhit error handling --- src/components/TurkHit/index.js | 8 +- src/containers/BuildingDetail/actions.js | 85 ++++++++++++++++++++-- src/containers/BuildingDetail/constants.js | 8 +- src/containers/BuildingDetail/index.js | 8 +- src/containers/BuildingDetail/reducer.js | 40 +++++++++- src/containers/BuildingDetail/sagas.js | 68 +++++++++-------- src/utils/request.js | 16 +--- 7 files changed, 172 insertions(+), 61 deletions(-) diff --git a/src/components/TurkHit/index.js b/src/components/TurkHit/index.js index bf9819fe..b7fd6037 100644 --- a/src/components/TurkHit/index.js +++ b/src/components/TurkHit/index.js @@ -4,13 +4,19 @@ import './styles.scss'; export default function TurkHit({ createHit, address, blocpower_id, hit }) { let hitStatus =
; - if (hit.status !== '') { + if (!hit.error.message && hit.status !== '') { hitStatus = (

HIT Id: {hit.id}

HIT Status: {hit.status}

); + } else { + hitStatus = ( +
+

{hit.error.message}

+
+ ); } return ( diff --git a/src/containers/BuildingDetail/actions.js b/src/containers/BuildingDetail/actions.js index 8eee882f..356c3411 100644 --- a/src/containers/BuildingDetail/actions.js +++ b/src/containers/BuildingDetail/actions.js @@ -1,18 +1,93 @@ import 'whatwg-fetch'; -import { FETCH_BUILDING_DETAIL, CREATE_HIT } from './constants'; +import { + LOAD_BUILDING_DETAIL, + LOAD_BUILDING_DETAIL_SUCCEES, + LOAD_BUILDING_DETAIL_ERROR, + CREATE_HIT, + CREATE_HIT_SUCCEES, + CREATE_HIT_ERROR, +} from './constants'; -function fetchBuildingDetail(blocPowerID) { +/** + * Load building details, this action starts the request saga + * + * @param blocPowerID The current blocPowerID + * @returns {object} An action object with a type of LOAD_BUILDING_DETAIL + * passing the building detail + */ +export function loadBuildingDetail(blocPowerID) { return { - type: FETCH_BUILDING_DETAIL, + type: LOAD_BUILDING_DETAIL, payload: blocPowerID, }; } -function createHit(formData) { +/** + * Dispatched when the building details are loaded by the request saga + * + * @param {object} buildingDetail The building data + * @returns {object} An action object with a type of + * LOAD_BUILDING_DETAIL_SUCCEES + */ +export function buildingDetailLoaded(buildingDetail) { + return { + type: LOAD_BUILDING_DETAIL_SUCCEES, + payload: buildingDetail, + }; +} + +/** + * Dispatched when loading the building detail fails + * + * @param {object} error The error + * + * @return {object} An action object with a type of + * LOAD_BUILDING_DETAIL_ERROR passing the error + */ +export function buildingDetailLoadingError(error) { + return { + type: LOAD_BUILDING_DETAIL_ERROR, + error, + }; +} + +/** + * Create mturk HIT, this action starts the request saga + * + * @param {object} formData The mturk form data + * @returns {object} An action object with a type of CREATE_HIT + */ +export function createHit(formData) { return { type: CREATE_HIT, payload: formData, }; } -export { fetchBuildingDetail, createHit }; +/** + * Dispatched when the mturk HIT was successfully created + * + * @param {object} hitData The created hit data + * @returns {object} An action object with a type of CREATE_HIT_SUCCEES + */ +export function createHitSuccess(hitData) { + return { + type: CREATE_HIT_SUCCEES, + payload: hitData, + }; +} + +/** + * Dispatched when creating the mturk HIT fails + * + * @param {object} error The error + * + * @return {object} An action object with a type of CREATE_HIT_ERROR + * passing the error + */ +export function createHitError(error) { + return { + type: CREATE_HIT_ERROR, + error, + }; +} diff --git a/src/containers/BuildingDetail/constants.js b/src/containers/BuildingDetail/constants.js index 467ee73e..873b9080 100644 --- a/src/containers/BuildingDetail/constants.js +++ b/src/containers/BuildingDetail/constants.js @@ -1,4 +1,6 @@ -export const FETCH_BUILDING_DETAIL = 'FETCH_BUILDING_DETAIL'; -export const FETCH_BUILDING_DETAIL_SUCCEEDED = 'FETCH_BUILDING_DETAIL_SUCCEEDED'; +export const LOAD_BUILDING_DETAIL = 'LOAD_BUILDING_DETAIL'; +export const LOAD_BUILDING_DETAIL_SUCCEES = 'LOAD_BUILDING_DETAIL_SUCCEES'; +export const LOAD_BUILDING_DETAIL_ERROR = 'LOAD_BUILDING_DETAIL_ERROR'; export const CREATE_HIT = 'CREATE_HIT'; -export const CREATE_HIT_SUCCEEDED = 'CREATE_HIT_SUCCEEDED'; +export const CREATE_HIT_SUCCEES = 'CREATE_HIT_SUCCEES'; +export const CREATE_HIT_ERROR = 'CREATE_HIT_ERROR'; diff --git a/src/containers/BuildingDetail/index.js b/src/containers/BuildingDetail/index.js index f0a53f2f..afa206c8 100644 --- a/src/containers/BuildingDetail/index.js +++ b/src/containers/BuildingDetail/index.js @@ -2,7 +2,7 @@ import React, { Component, PropTypes } from 'react'; import { connect } from 'react-redux'; import { bindActionCreators } from 'redux'; -import { fetchBuildingDetail, createHit } from './actions'; +import { loadBuildingDetail, createHit } from './actions'; import './styles.scss'; import SideBarDetail from '../../components/SideBarDetail'; import TurkHit from '../../components/TurkHit'; @@ -10,7 +10,7 @@ import TurkHit from '../../components/TurkHit'; class BuildingDetail extends Component { componentDidMount() { - this.props.fetchBuildingDetail(this.props.params.blocPowerID); + this.props.loadBuildingDetail(this.props.params.blocPowerID); } render() { @@ -66,13 +66,13 @@ BuildingDetail.propTypes = { }), }), params: PropTypes.objectOf(PropTypes.string), - fetchBuildingDetail: PropTypes.func, + loadBuildingDetail: PropTypes.func, createHit: PropTypes.func, main: React.PropTypes.element, }; function mapDispatchToProps(dispatch) { - return bindActionCreators({ fetchBuildingDetail, createHit }, dispatch); + return bindActionCreators({ loadBuildingDetail, createHit }, dispatch); } function mapStateToProps({ buildingDetail }) { diff --git a/src/containers/BuildingDetail/reducer.js b/src/containers/BuildingDetail/reducer.js index d4272add..014f978f 100644 --- a/src/containers/BuildingDetail/reducer.js +++ b/src/containers/BuildingDetail/reducer.js @@ -1,23 +1,55 @@ -import { FETCH_BUILDING_DETAIL_SUCCEEDED, CREATE_HIT_SUCCEEDED } from './constants'; +import { + LOAD_BUILDING_DETAIL_SUCCEES, + CREATE_HIT, + CREATE_HIT_SUCCEES, + CREATE_HIT_ERROR, +} from './constants'; const initState = { hit: { id: '', status: '', + loading: false, + error: false, }, }; export default function (state = initState, action) { switch (action.type) { - case FETCH_BUILDING_DETAIL_SUCCEEDED: - return { ...state, overview: action.payload.data.building }; + case LOAD_BUILDING_DETAIL_SUCCEES: + return { + ...state, + overview: action.payload.data.building, + }; - case CREATE_HIT_SUCCEEDED: + case CREATE_HIT: return { ...state, hit: { + ...state.hit, + loading: true, + error: false, + }, + }; + + case CREATE_HIT_SUCCEES: + return { + ...state, + hit: { + ...state.hit, id: action.payload.data.hit_id, status: 'Submitted', + loading: false, + }, + }; + + case CREATE_HIT_ERROR: + return { + ...state, + hit: { + ...state.hit, + loading: false, + error: action.error, }, }; diff --git a/src/containers/BuildingDetail/sagas.js b/src/containers/BuildingDetail/sagas.js index 947dde91..028e1afe 100644 --- a/src/containers/BuildingDetail/sagas.js +++ b/src/containers/BuildingDetail/sagas.js @@ -1,66 +1,72 @@ import { call, put, takeEvery } from 'redux-saga/effects'; +import request from '../../utils/request'; + import { - FETCH_BUILDING_DETAIL, - FETCH_BUILDING_DETAIL_SUCCEEDED, + LOAD_BUILDING_DETAIL, CREATE_HIT, - CREATE_HIT_SUCCEEDED, } from './constants'; -import request from '../../utils/request'; -const ROOT_URL = `${process.env.REACT_APP_BUILDING_SERVICE}`; -const HEADERS = new Headers({ 'x-blocpower-app-key': process.env.REACT_APP_KEY }); +import { + buildingDetailLoaded, + createHitSuccess, + createHitError, +} from './actions'; + +const BUILDING_SERVICE_URL = `${process.env.REACT_APP_BUILDING_SERVICE}`; +const HEADERS = new Headers({ + 'x-blocpower-app-key': process.env.REACT_APP_KEY, +}); /** * Detail page request/response handler + * + * @param {object} action blocPowerId of building */ -function* fetchDetailPage(action) { - const data = yield call(request, `${ROOT_URL}/building/${action.payload}`, { - method: 'GET', - headers: HEADERS, - }); +function* getBuildingDetail(action) { + const blocPowerId = action.payload; + const data = yield call( + request, + `${BUILDING_SERVICE_URL}/building/${blocPowerId}`, { + method: 'GET', + headers: HEADERS, + } + ); if (!data.err) { - yield put({ type: FETCH_BUILDING_DETAIL_SUCCEEDED, payload: data }); + yield put(buildingDetailLoaded(data)); } else { // TODO handle error properly console.error(data.err); } } +/** + * Mechanical Turk HIT creation request/response handler + * + * @param {object} action Form data of mechanical turk job + */ function* createHit(action) { - const data = yield call(request, `${ROOT_URL}/turkhit/`, { + const turkHitFormData = action.payload; + const data = yield call(request, `${BUILDING_SERVICE_URL}/turkhit/`, { method: 'POST', headers: HEADERS, - body: JSON.stringify(action.payload), + body: JSON.stringify(turkHitFormData), }); if (!data.err) { - yield put({ type: CREATE_HIT_SUCCEEDED, payload: data }); + yield put(createHitSuccess(data)); } else { - // TODO handle error properly - console.error(data.err); + yield put(createHitError(data.err)); } } /** - * Watches for FETCH_BUILDING_DETAIL & CREATE_HIT actions and calls + * Watches for LOAD_BUILDING_DETAIL & CREATE_HIT actions and calls * the appropriate handler function. */ function* buildingDetailWatcher() { - yield takeEvery(FETCH_BUILDING_DETAIL, fetchDetailPage); + yield takeEvery(LOAD_BUILDING_DETAIL, getBuildingDetail); yield takeEvery(CREATE_HIT, createHit); } -/** - * Root saga manages watcher lifecycle - */ -// export function* githubData() { - // Fork watcher so we can continue execution - // const watcher = yield fork(getReposWatcher); - - // Suspend execution until location changes - // yield take(LOCATION_CHANGE); - // yield cancel(watcher); -// } - export default buildingDetailWatcher; diff --git a/src/utils/request.js b/src/utils/request.js index 958fb3fc..fae139a8 100644 --- a/src/utils/request.js +++ b/src/utils/request.js @@ -1,25 +1,15 @@ import 'whatwg-fetch'; -/** - * Parses the JSON returned by a network request - * - * @param {object} response A response from a network request - * - * @return {object} The parsed JSON from the request - */ -function parseJSON(response) { - return response.json(); -} /** * Checks if a network request came back fine, and throws an error if not * - * @param {object} response A response from a network request + * @param {object} response A response from a network request * * @return {object|undefined} Returns either the response, or throws an error */ function checkStatus(response) { - if (response.status >= 200 && response.status < 300) { + if (response.ok) { return response; } @@ -39,7 +29,7 @@ function checkStatus(response) { export default function request(url, options) { return fetch(url, options) .then(checkStatus) - .then(parseJSON) + .then(response => response.json()) .then(data => ({ data })) .catch(err => ({ err })); } -- GitLab From 1bd6dd9b78dd2e892e937052dea1c5c1fc571adc Mon Sep 17 00:00:00 2001 From: Alessandro DiMarco Date: Tue, 17 Jan 2017 03:07:19 -0500 Subject: [PATCH 12/15] Add error handling to loading building detail --- src/components/TurkHit/index.js | 2 ++ src/containers/BuildingDetail/index.js | 39 ++++++++++++++++-------- src/containers/BuildingDetail/reducer.js | 32 ++++++++++++++++++- src/containers/BuildingDetail/sagas.js | 4 +-- 4 files changed, 61 insertions(+), 16 deletions(-) diff --git a/src/components/TurkHit/index.js b/src/components/TurkHit/index.js index b7fd6037..e549d09d 100644 --- a/src/components/TurkHit/index.js +++ b/src/components/TurkHit/index.js @@ -39,5 +39,7 @@ TurkHit.propTypes = { hit: PropTypes.shape({ id: PropTypes.string, status: PropTypes.string, + loading: PropTypes.boolean, + error: PropTypes.boolean, }), }; diff --git a/src/containers/BuildingDetail/index.js b/src/containers/BuildingDetail/index.js index afa206c8..34efb286 100644 --- a/src/containers/BuildingDetail/index.js +++ b/src/containers/BuildingDetail/index.js @@ -17,24 +17,33 @@ class BuildingDetail extends Component { let mainContent =
; let hit =
; - if (this.props.buildingDetail.overview) { - switch (this.props.main.type.name) { - case 'BuildingOverview': + switch (this.props.main.type.name) { + + case 'BuildingOverview': + if (this.props.buildingDetail.overview.error) { + mainContent = ( +

+ {this.props.buildingDetail.overview.error.message} +

+ ); + } else { mainContent = React.cloneElement(this.props.main, { building: this.props.buildingDetail.overview, }); - break; - default: - return
; - } + hit = ( + + ); + } + break; - hit = (); + default: + return
; } return ( @@ -59,10 +68,14 @@ BuildingDetail.propTypes = { blocpower_id: PropTypes.number, borough: PropTypes.string, zipcode: PropTypes.number, + loading: PropTypes.boolean, + error: PropTypes.boolean, }), hit: PropTypes.shape({ id: PropTypes.string, status: PropTypes.string, + loading: PropTypes.boolean, + error: PropTypes.boolean, }), }), params: PropTypes.objectOf(PropTypes.string), diff --git a/src/containers/BuildingDetail/reducer.js b/src/containers/BuildingDetail/reducer.js index 014f978f..69edf36c 100644 --- a/src/containers/BuildingDetail/reducer.js +++ b/src/containers/BuildingDetail/reducer.js @@ -1,11 +1,17 @@ import { + LOAD_BUILDING_DETAIL, LOAD_BUILDING_DETAIL_SUCCEES, + LOAD_BUILDING_DETAIL_ERROR, CREATE_HIT, CREATE_HIT_SUCCEES, CREATE_HIT_ERROR, } from './constants'; const initState = { + overview: { + loading: false, + error: false, + }, hit: { id: '', status: '', @@ -16,10 +22,34 @@ const initState = { export default function (state = initState, action) { switch (action.type) { + case LOAD_BUILDING_DETAIL: + return { + ...state, + overview: { + ...state.overview, + loading: true, + error: false, + }, + }; + case LOAD_BUILDING_DETAIL_SUCCEES: return { ...state, - overview: action.payload.data.building, + overview: { + ...state.overview, + ...action.payload.data.building, + loading: false, + }, + }; + + case LOAD_BUILDING_DETAIL_ERROR: + return { + ...state, + overview: { + ...state.overview, + loading: true, + error: action.error, + }, }; case CREATE_HIT: diff --git a/src/containers/BuildingDetail/sagas.js b/src/containers/BuildingDetail/sagas.js index 028e1afe..319b938a 100644 --- a/src/containers/BuildingDetail/sagas.js +++ b/src/containers/BuildingDetail/sagas.js @@ -8,6 +8,7 @@ import { import { buildingDetailLoaded, + buildingDetailLoadingError, createHitSuccess, createHitError, } from './actions'; @@ -35,8 +36,7 @@ function* getBuildingDetail(action) { if (!data.err) { yield put(buildingDetailLoaded(data)); } else { - // TODO handle error properly - console.error(data.err); + yield put(buildingDetailLoadingError(data.err)); } } -- GitLab From c01360a71499264ecd75ebc5fce239ccaddb3e02 Mon Sep 17 00:00:00 2001 From: Alessandro DiMarco Date: Tue, 17 Jan 2017 12:24:45 -0500 Subject: [PATCH 13/15] Update action.payload in reducers --- src/containers/BuildingDetail/reducer.js | 2 +- src/containers/BuildingList/index.js | 1 - src/containers/BuildingList/reducer.js | 2 +- src/reducers.js | 4 +--- src/store.js | 6 +++--- 5 files changed, 6 insertions(+), 9 deletions(-) diff --git a/src/containers/BuildingDetail/reducer.js b/src/containers/BuildingDetail/reducer.js index 69edf36c..457c6ed7 100644 --- a/src/containers/BuildingDetail/reducer.js +++ b/src/containers/BuildingDetail/reducer.js @@ -37,7 +37,7 @@ export default function (state = initState, action) { ...state, overview: { ...state.overview, - ...action.payload.data.building, + ...action.payload.data, loading: false, }, }; diff --git a/src/containers/BuildingList/index.js b/src/containers/BuildingList/index.js index 90de57dc..3e775c91 100644 --- a/src/containers/BuildingList/index.js +++ b/src/containers/BuildingList/index.js @@ -1,4 +1,3 @@ - import React, { Component, PropTypes } from 'react'; import { connect } from 'react-redux'; import { bindActionCreators } from 'redux'; diff --git a/src/containers/BuildingList/reducer.js b/src/containers/BuildingList/reducer.js index 6c804205..13acd7e5 100644 --- a/src/containers/BuildingList/reducer.js +++ b/src/containers/BuildingList/reducer.js @@ -3,7 +3,7 @@ import { FETCH_BUILDINGS, SEARCH_TERM } from './constants'; export default function (state = {}, action) { switch (action.type) { case FETCH_BUILDINGS: - return { ...state, buildings: action.payload.data }; + return { ...state, buildings: action.payload.buildings }; case SEARCH_TERM: return { ...state, term: action.payload }; diff --git a/src/reducers.js b/src/reducers.js index af7f5c9b..3568c2e9 100644 --- a/src/reducers.js +++ b/src/reducers.js @@ -4,10 +4,8 @@ import { routerReducer } from 'react-router-redux'; import BuildingListReducer from './containers/BuildingList/reducer'; import BuildingDetailReducer from './containers/BuildingDetail/reducer'; -const rootReducer = combineReducers({ +export default combineReducers({ routing: routerReducer, buildingList: BuildingListReducer, buildingDetail: BuildingDetailReducer, }); - -export default rootReducer; diff --git a/src/store.js b/src/store.js index b6e4b001..83daf1d4 100644 --- a/src/store.js +++ b/src/store.js @@ -7,8 +7,6 @@ import createSagaMiddleware from 'redux-saga'; import rootReducer from './reducers'; import rootSaga from './sagas'; -const sagaMiddleware = createSagaMiddleware(); - /* eslint-disable no-underscore-dangle */ const composeEnhancers = process.env.NODE_ENV !== 'production' && @@ -17,12 +15,14 @@ const composeEnhancers = window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__({ // Specify here name, actionsBlacklist, actionsCreators and other options }) : compose; +/* eslint-enable */ + +const sagaMiddleware = createSagaMiddleware(); const enhancer = composeEnhancers( applyMiddleware(sagaMiddleware, ReduxPromise), // other store enhancers if any ); -/* eslint-enable */ const store = createStore(rootReducer, enhancer); const history = syncHistoryWithStore(browserHistory, store); -- GitLab From 8e91687404371a21110ee0a20ec00c7723d051ad Mon Sep 17 00:00:00 2001 From: Alessandro DiMarco Date: Tue, 17 Jan 2017 18:01:03 -0500 Subject: [PATCH 14/15] Update SASS loader in webpack prod --- config/webpack.config.prod.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/config/webpack.config.prod.js b/config/webpack.config.prod.js index f63d0f6d..0eb878fa 100644 --- a/config/webpack.config.prod.js +++ b/config/webpack.config.prod.js @@ -135,7 +135,7 @@ module.exports = { test: /\.scss$/, loader: ExtractTextPlugin.extract( 'style', - 'css?modules&importLoaders=1&-autoprefixer!postcss!resolve-url!sass' + 'css?importLoaders=1!postcss!sass' ) }, // JSON is not enabled by default in Webpack but both Node and Browserify -- GitLab From 08756de71065915a44d0bed3cfd250d9d02d7c0c Mon Sep 17 00:00:00 2001 From: Alessandro DiMarco Date: Wed, 18 Jan 2017 12:02:09 -0500 Subject: [PATCH 15/15] Remove switch statement --- src/containers/BuildingDetail/index.js | 46 +++++++++++--------------- src/routes.js | 2 +- 2 files changed, 20 insertions(+), 28 deletions(-) diff --git a/src/containers/BuildingDetail/index.js b/src/containers/BuildingDetail/index.js index 34efb286..d8789ee3 100644 --- a/src/containers/BuildingDetail/index.js +++ b/src/containers/BuildingDetail/index.js @@ -17,33 +17,25 @@ class BuildingDetail extends Component { let mainContent =
; let hit =
; - switch (this.props.main.type.name) { + if (this.props.buildingDetail.overview.error) { + mainContent = ( +

+ {this.props.buildingDetail.overview.error.message} +

+ ); + } else { + mainContent = React.cloneElement(this.props.children, { + building: this.props.buildingDetail.overview, + }); - case 'BuildingOverview': - if (this.props.buildingDetail.overview.error) { - mainContent = ( -

- {this.props.buildingDetail.overview.error.message} -

- ); - } else { - mainContent = React.cloneElement(this.props.main, { - building: this.props.buildingDetail.overview, - }); - - hit = ( - - ); - } - break; - - default: - return
; + hit = ( + + ); } return ( @@ -81,7 +73,7 @@ BuildingDetail.propTypes = { params: PropTypes.objectOf(PropTypes.string), loadBuildingDetail: PropTypes.func, createHit: PropTypes.func, - main: React.PropTypes.element, + children: React.PropTypes.element, }; function mapDispatchToProps(dispatch) { diff --git a/src/routes.js b/src/routes.js index 561a2e0e..24075922 100644 --- a/src/routes.js +++ b/src/routes.js @@ -10,7 +10,7 @@ export default ( - + -- GitLab