From d2009b54f84dc5c83019fea9cc932f1c937e95e0 Mon Sep 17 00:00:00 2001 From: Alessandro DiMarco Date: Thu, 2 Feb 2017 12:29:05 -0500 Subject: [PATCH 1/7] Add sagas to search container --- src/containers/SearchBar/actions.js | 56 +++++++++++++++++++-------- src/containers/SearchBar/constants.js | 3 ++ src/containers/SearchBar/index.js | 15 ++++--- src/containers/SearchBar/reducer.js | 39 ++++++++++++++++--- src/containers/SearchBar/sagas.js | 39 +++++++++++++++++++ src/sagas.js | 2 + 6 files changed, 126 insertions(+), 28 deletions(-) create mode 100644 src/containers/SearchBar/sagas.js diff --git a/src/containers/SearchBar/actions.js b/src/containers/SearchBar/actions.js index 0fd5e8a0..c33387ec 100644 --- a/src/containers/SearchBar/actions.js +++ b/src/containers/SearchBar/actions.js @@ -1,28 +1,50 @@ -import 'whatwg-fetch'; -import { FETCH_BUILDINGS, SEARCH_TERM } from './constants'; -import { getHeaders, buildingsURL } from '../../utils/rest_services'; +import { + SEARCH_TERM, + LOAD_BUILDINGS, + LOAD_BUILDINGS_SUCCESS, + LOAD_BUILDINGS_ERROR, +} from './constants'; -function fetchBuildings(address) { - const url = `${buildingsURL}?address=${address}`; - - const init = { - method: 'GET', - headers: getHeaders(), +/** + * Update search term with new address + * + * @param {string} address The new search address + * @returns {object} An action object with a type of SEARCH_TERM + * and the address + */ +export function searchTerm(address) { + return { + type: SEARCH_TERM, + address, }; +} +/** + * Load the list of buildings, this action starts the request saga + * + * @param {string} address The address to be searched + * @returns {object} An action object with a type of LOAD_BUILDINGS + * and the address + */ +export function loadBuildings(address) { return { - type: FETCH_BUILDINGS, - payload: fetch(url, init).then(response => - response.json() - ), + type: LOAD_BUILDINGS, + address, }; } -function searchTerm(address) { +// TODO Add doctring +export function buildingsLoaded(buildings) { return { - type: SEARCH_TERM, - payload: address, + type: LOAD_BUILDINGS_SUCCESS, + payload: buildings, }; } -export { fetchBuildings, searchTerm }; +// TODO Add doctring +export function buildingsLoadingError(error) { + return { + type: LOAD_BUILDINGS_ERROR, + error, + }; +} diff --git a/src/containers/SearchBar/constants.js b/src/containers/SearchBar/constants.js index a445948a..3c571d6d 100644 --- a/src/containers/SearchBar/constants.js +++ b/src/containers/SearchBar/constants.js @@ -1,2 +1,5 @@ export const FETCH_BUILDINGS = 'FETCH_BUILDINGS'; export const SEARCH_TERM = 'SEARCH_TERM'; +export const LOAD_BUILDINGS = 'LOAD_BUILDINGS'; +export const LOAD_BUILDINGS_SUCCESS = 'LOAD_BUILDINGS_SUCCESS'; +export const LOAD_BUILDINGS_ERROR = 'LOAD_BUILDINGS_ERROR'; diff --git a/src/containers/SearchBar/index.js b/src/containers/SearchBar/index.js index 5000ed5d..6374e4a1 100644 --- a/src/containers/SearchBar/index.js +++ b/src/containers/SearchBar/index.js @@ -2,7 +2,7 @@ import React, { Component, PropTypes } from 'react'; import { connect } from 'react-redux'; import { bindActionCreators } from 'redux'; -import { fetchBuildings, searchTerm } from './actions'; +import { searchTerm, loadBuildings } from './actions'; import './styles.scss'; import { SearchSVG } from '../../components/bpl'; @@ -21,7 +21,9 @@ class BuildingList extends Component { } onInputChange(event) { - this.setState({ term: event.target.value }); + this.setState({ term: event.target.value }, () => { + this.getBuildings(); + }); } onFormSubmit(event) { @@ -31,7 +33,7 @@ class BuildingList extends Component { getBuildings() { this.props.searchTerm(this.state.term); - this.props.fetchBuildings(this.state.term); + this.props.loadBuildings(this.state.term); } render() { @@ -69,12 +71,15 @@ BuildingList.propTypes = { buildingList: PropTypes.shape({ term: PropTypes.string, }), - fetchBuildings: PropTypes.func, searchTerm: PropTypes.func, + loadBuildings: PropTypes.func, }; function mapDispatchToProps(dispatch) { - return bindActionCreators({ fetchBuildings, searchTerm }, dispatch); + return bindActionCreators({ + searchTerm, + loadBuildings, + }, dispatch); } function mapStateToProps({ buildingList }) { diff --git a/src/containers/SearchBar/reducer.js b/src/containers/SearchBar/reducer.js index 230de775..401ca627 100644 --- a/src/containers/SearchBar/reducer.js +++ b/src/containers/SearchBar/reducer.js @@ -1,17 +1,44 @@ -import { FETCH_BUILDINGS, SEARCH_TERM } from './constants'; +import { + SEARCH_TERM, + LOAD_BUILDINGS, + LOAD_BUILDINGS_SUCCESS, + LOAD_BUILDINGS_ERROR, +} from './constants'; -// TODO remove hard coded search term +// TODO: remove hard coded search term const initState = { term: '107 broadway', + buildings: [], + loading: false, + error: false, }; export default function (state = initState, action) { switch (action.type) { - case FETCH_BUILDINGS: - return { ...state, buildings: action.payload.buildings }; case SEARCH_TERM: - return { ...state, term: action.payload }; - + return { + ...state, + term: action.address, + }; + case LOAD_BUILDINGS: + return { + ...state, + loading: true, + error: false, + }; + case LOAD_BUILDINGS_SUCCESS: + return { + ...state, + buildings: action.payload.buildings, + loading: false, + error: false, + }; + case LOAD_BUILDINGS_ERROR: + return { + ...state, + loading: false, + error: action.error, + }; default: return state; } diff --git a/src/containers/SearchBar/sagas.js b/src/containers/SearchBar/sagas.js new file mode 100644 index 00000000..09feb54e --- /dev/null +++ b/src/containers/SearchBar/sagas.js @@ -0,0 +1,39 @@ +import { call, put, takeLatest } from 'redux-saga/effects'; +import request from '../../utils/request'; +import { getHeaders, buildingsURL } from '../../utils/rest_services'; + +import { + LOAD_BUILDINGS, +} from './constants'; + +import { + buildingsLoaded, + buildingsLoadingError, +} from './actions'; + +/** + * TODO: doctring + */ +function* getBuildings(action) { + const address = action.address; + const res = yield call( + request, + `${buildingsURL}?address=${address}&limit=300`, { + method: 'GET', + headers: getHeaders(), + } + ); + + if (!res.err) { + yield put(buildingsLoaded(res.data)); + } else { + yield put(buildingsLoadingError(res.error)); + } +} + +/** + * TODO: docstring + */ +export default function* buildingsWatcher() { + yield takeLatest(LOAD_BUILDINGS, getBuildings); +} diff --git a/src/sagas.js b/src/sagas.js index 1f35dcd1..1d1af9dc 100644 --- a/src/sagas.js +++ b/src/sagas.js @@ -1,3 +1,4 @@ +import buildingsSearchSaga from './containers/SearchBar/sagas'; import buildingOverviewSaga from './containers/BuildingOverview/sagas'; import dimensionsSaga from './containers/Dimensions/sagas'; @@ -5,5 +6,6 @@ export default function* rootSaga() { yield [ buildingOverviewSaga(), dimensionsSaga(), + buildingsSearchSaga(), ]; } -- GitLab From 31ebea2e1aeaf6d313239c766b7e9c25e3fa72b5 Mon Sep 17 00:00:00 2001 From: Alessandro DiMarco Date: Thu, 2 Feb 2017 13:17:44 -0500 Subject: [PATCH 2/7] Add loading gif to search and error box --- src/components/BuildingListTable/index.js | 10 +++++++++- src/containers/SearchBar/index.js | 14 +++++++++++++- src/containers/SearchBar/ring.svg | 1 + src/containers/SearchBar/sagas.js | 2 +- src/screens/HomePage/index.js | 22 ++++++++++++++++++---- src/screens/HomePage/styles.css | 0 src/screens/Login/styles.css | 0 7 files changed, 42 insertions(+), 7 deletions(-) create mode 100644 src/containers/SearchBar/ring.svg create mode 100644 src/screens/HomePage/styles.css create mode 100644 src/screens/Login/styles.css diff --git a/src/components/BuildingListTable/index.js b/src/components/BuildingListTable/index.js index 615744f0..8ffa72a2 100644 --- a/src/components/BuildingListTable/index.js +++ b/src/components/BuildingListTable/index.js @@ -2,7 +2,11 @@ import React, { PropTypes } from 'react'; import { browserHistory } from 'react-router'; import './styles.scss'; -export default function BuildingListTable({ buildings }) { +export default function BuildingListTable({ buildings, error }) { + if (error) { + return
; + } + if (!buildings || buildings.length === 0) { return
None
; } @@ -57,4 +61,8 @@ BuildingListTable.propTypes = { borough: PropTypes.string, zipcode: PropTypes.number, })), + error: PropTypes.oneOfType([ + PropTypes.instanceOf(Error), + PropTypes.bool, + ]), }; diff --git a/src/containers/SearchBar/index.js b/src/containers/SearchBar/index.js index 6374e4a1..e435fcbc 100644 --- a/src/containers/SearchBar/index.js +++ b/src/containers/SearchBar/index.js @@ -5,7 +5,7 @@ import { bindActionCreators } from 'redux'; import { searchTerm, loadBuildings } from './actions'; import './styles.scss'; import { SearchSVG } from '../../components/bpl'; - +import loadingRing from './ring.svg'; class BuildingList extends Component { constructor(props) { @@ -37,6 +37,7 @@ class BuildingList extends Component { } render() { + const loading = this.props.buildingList.loading; return (
@@ -62,6 +63,12 @@ class BuildingList extends Component {
+ loading
); } @@ -70,6 +77,11 @@ class BuildingList extends Component { BuildingList.propTypes = { buildingList: PropTypes.shape({ term: PropTypes.string, + loading: PropTypes.bool, + error: React.PropTypes.oneOfType([ + PropTypes.instanceOf(Error), + PropTypes.bool, + ]), }), searchTerm: PropTypes.func, loadBuildings: PropTypes.func, diff --git a/src/containers/SearchBar/ring.svg b/src/containers/SearchBar/ring.svg new file mode 100644 index 00000000..46d2a4fd --- /dev/null +++ b/src/containers/SearchBar/ring.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/containers/SearchBar/sagas.js b/src/containers/SearchBar/sagas.js index 09feb54e..0d056cac 100644 --- a/src/containers/SearchBar/sagas.js +++ b/src/containers/SearchBar/sagas.js @@ -27,7 +27,7 @@ function* getBuildings(action) { if (!res.err) { yield put(buildingsLoaded(res.data)); } else { - yield put(buildingsLoadingError(res.error)); + yield put(buildingsLoadingError(res.err)); } } diff --git a/src/screens/HomePage/index.js b/src/screens/HomePage/index.js index 31aa3ccc..010cbcbf 100644 --- a/src/screens/HomePage/index.js +++ b/src/screens/HomePage/index.js @@ -2,14 +2,23 @@ import React, { PropTypes } from 'react'; import { connect } from 'react-redux'; import NavBar from '../../components/NavBar'; -import BuildingList from '../../containers/SearchBar'; +import SearchBar from '../../containers/SearchBar'; import BuildingListTable from '../../components/BuildingListTable'; function HomePage({ buildingList }) { return (
- - + +
+ There was a problem retrieving the buildings. | {buildingList.error.message} +
+
); } @@ -24,9 +33,14 @@ HomePage.propTypes = { lot_id: PropTypes.number, borough: PropTypes.string, zipcode: PropTypes.number, - }) + }), ), term: PropTypes.string, + loading: PropTypes.bool, + error: PropTypes.oneOfType([ + PropTypes.instanceOf(Error), + PropTypes.bool, + ]), }), }; diff --git a/src/screens/HomePage/styles.css b/src/screens/HomePage/styles.css new file mode 100644 index 00000000..e69de29b diff --git a/src/screens/Login/styles.css b/src/screens/Login/styles.css new file mode 100644 index 00000000..e69de29b -- GitLab From 2f6d07f107e8daeb3d3a3ab0f49fe27457ceb872 Mon Sep 17 00:00:00 2001 From: Alessandro DiMarco Date: Thu, 2 Feb 2017 13:21:56 -0500 Subject: [PATCH 3/7] Rename styles from scss to css --- src/components/BuildingListTable/index.js | 2 +- src/components/BuildingListTable/{styles.scss => styles.css} | 0 src/components/BuildingOverviewTop/index.js | 2 +- src/components/BuildingOverviewTop/{styles.scss => styles.css} | 0 src/components/{SideBarDetail/styles.scss => NavBar/style.css} | 0 src/components/SideBarDetail/index.js | 2 +- .../{TurkHit/styles.scss => SideBarDetail/styles.css} | 0 src/components/TurkHit/index.js | 2 +- .../styles.scss => components/TurkHit/styles.css} | 0 src/containers/BuildingOverview/index.js | 2 +- .../styles.scss => containers/BuildingOverview/styles.css} | 0 src/containers/GoogleLogin/styles.css | 0 src/containers/SearchBar/index.js | 2 +- src/containers/SearchBar/{styles.scss => styles.css} | 0 src/screens/BuildingDetail/styles.css | 0 src/screens/NotFound/styles.css | 0 16 files changed, 6 insertions(+), 6 deletions(-) rename src/components/BuildingListTable/{styles.scss => styles.css} (100%) rename src/components/BuildingOverviewTop/{styles.scss => styles.css} (100%) rename src/components/{SideBarDetail/styles.scss => NavBar/style.css} (100%) rename src/components/{TurkHit/styles.scss => SideBarDetail/styles.css} (100%) rename src/{containers/BuildingOverview/styles.scss => components/TurkHit/styles.css} (100%) rename src/{screens/BuildingDetail/styles.scss => containers/BuildingOverview/styles.css} (100%) create mode 100644 src/containers/GoogleLogin/styles.css rename src/containers/SearchBar/{styles.scss => styles.css} (100%) create mode 100644 src/screens/BuildingDetail/styles.css create mode 100644 src/screens/NotFound/styles.css diff --git a/src/components/BuildingListTable/index.js b/src/components/BuildingListTable/index.js index 8ffa72a2..c5cedb2c 100644 --- a/src/components/BuildingListTable/index.js +++ b/src/components/BuildingListTable/index.js @@ -1,6 +1,6 @@ import React, { PropTypes } from 'react'; import { browserHistory } from 'react-router'; -import './styles.scss'; +import './styles.css'; export default function BuildingListTable({ buildings, error }) { if (error) { diff --git a/src/components/BuildingListTable/styles.scss b/src/components/BuildingListTable/styles.css similarity index 100% rename from src/components/BuildingListTable/styles.scss rename to src/components/BuildingListTable/styles.css diff --git a/src/components/BuildingOverviewTop/index.js b/src/components/BuildingOverviewTop/index.js index 319aed1e..db327353 100644 --- a/src/components/BuildingOverviewTop/index.js +++ b/src/components/BuildingOverviewTop/index.js @@ -1,5 +1,5 @@ import React, { PropTypes } from 'react'; -import './styles.scss'; +import './styles.css'; export default function BuildingOverview({ building }) { return ( diff --git a/src/components/BuildingOverviewTop/styles.scss b/src/components/BuildingOverviewTop/styles.css similarity index 100% rename from src/components/BuildingOverviewTop/styles.scss rename to src/components/BuildingOverviewTop/styles.css diff --git a/src/components/SideBarDetail/styles.scss b/src/components/NavBar/style.css similarity index 100% rename from src/components/SideBarDetail/styles.scss rename to src/components/NavBar/style.css diff --git a/src/components/SideBarDetail/index.js b/src/components/SideBarDetail/index.js index c801501d..861db36e 100644 --- a/src/components/SideBarDetail/index.js +++ b/src/components/SideBarDetail/index.js @@ -1,6 +1,6 @@ import React, { PropTypes } from 'react'; import { Link } from 'react-router'; -import './styles.scss'; +import './styles.css'; export default function SideBarDetail({ buildingId }) { const rootURL = `/buildings/${buildingId}`; diff --git a/src/components/TurkHit/styles.scss b/src/components/SideBarDetail/styles.css similarity index 100% rename from src/components/TurkHit/styles.scss rename to src/components/SideBarDetail/styles.css diff --git a/src/components/TurkHit/index.js b/src/components/TurkHit/index.js index 517143a2..2901e764 100644 --- a/src/components/TurkHit/index.js +++ b/src/components/TurkHit/index.js @@ -1,6 +1,6 @@ import React, { PropTypes, Component } from 'react'; import defaultForm from './defaultForm'; -import './styles.scss'; +import './styles.css'; import turkHitPropTypes from '../../containers/Dimensions/propTypes'; import turkStatus from './turkStatus'; diff --git a/src/containers/BuildingOverview/styles.scss b/src/components/TurkHit/styles.css similarity index 100% rename from src/containers/BuildingOverview/styles.scss rename to src/components/TurkHit/styles.css diff --git a/src/containers/BuildingOverview/index.js b/src/containers/BuildingOverview/index.js index f5561817..56666f90 100644 --- a/src/containers/BuildingOverview/index.js +++ b/src/containers/BuildingOverview/index.js @@ -5,7 +5,7 @@ import buildingDetailPropTypes from './buildingDetailPropType'; import { loadBuildingDetail } from './actions'; -import './styles.scss'; +import './styles.css'; import BuildingOverviewTop from '../../components/BuildingOverviewTop'; diff --git a/src/screens/BuildingDetail/styles.scss b/src/containers/BuildingOverview/styles.css similarity index 100% rename from src/screens/BuildingDetail/styles.scss rename to src/containers/BuildingOverview/styles.css diff --git a/src/containers/GoogleLogin/styles.css b/src/containers/GoogleLogin/styles.css new file mode 100644 index 00000000..e69de29b diff --git a/src/containers/SearchBar/index.js b/src/containers/SearchBar/index.js index e435fcbc..de62048b 100644 --- a/src/containers/SearchBar/index.js +++ b/src/containers/SearchBar/index.js @@ -3,7 +3,7 @@ import { connect } from 'react-redux'; import { bindActionCreators } from 'redux'; import { searchTerm, loadBuildings } from './actions'; -import './styles.scss'; +import './styles.css'; import { SearchSVG } from '../../components/bpl'; import loadingRing from './ring.svg'; diff --git a/src/containers/SearchBar/styles.scss b/src/containers/SearchBar/styles.css similarity index 100% rename from src/containers/SearchBar/styles.scss rename to src/containers/SearchBar/styles.css diff --git a/src/screens/BuildingDetail/styles.css b/src/screens/BuildingDetail/styles.css new file mode 100644 index 00000000..e69de29b diff --git a/src/screens/NotFound/styles.css b/src/screens/NotFound/styles.css new file mode 100644 index 00000000..e69de29b -- GitLab From dcd2552061e95e6dcba36c56f22266814714b834 Mon Sep 17 00:00:00 2001 From: Alessandro DiMarco Date: Thu, 2 Feb 2017 13:27:14 -0500 Subject: [PATCH 4/7] Remove dummy routes and components --- src/components/dummyComponent.js | 5 ----- src/routes.js | 5 ----- 2 files changed, 10 deletions(-) delete mode 100644 src/components/dummyComponent.js diff --git a/src/components/dummyComponent.js b/src/components/dummyComponent.js deleted file mode 100644 index 2cec953e..00000000 --- a/src/components/dummyComponent.js +++ /dev/null @@ -1,5 +0,0 @@ -import React from 'react'; - -export default function dummyComponent() { - return

Coming Soon!

; -} diff --git a/src/routes.js b/src/routes.js index 4410848d..e6a44002 100644 --- a/src/routes.js +++ b/src/routes.js @@ -9,9 +9,7 @@ import HomePage from './screens/HomePage'; import BuildingDetail from './screens/BuildingDetail'; import BuildingOverview from './containers/BuildingOverview'; import Dimensions from './containers/Dimensions'; - import Utilities from './components/Utilities'; -import Dummy from './components/dummyComponent'; export default ( @@ -23,10 +21,7 @@ export default ( - - - -- GitLab From d6b7b93cbe4d95a3c1919c2f668c4dce0200f144 Mon Sep 17 00:00:00 2001 From: Alessandro DiMarco Date: Thu, 2 Feb 2017 13:34:50 -0500 Subject: [PATCH 5/7] Create render method for definitions --- src/components/TurkHit/index.js | 37 ++++++++++++++++++++------------- 1 file changed, 23 insertions(+), 14 deletions(-) diff --git a/src/components/TurkHit/index.js b/src/components/TurkHit/index.js index 2901e764..80b75e4a 100644 --- a/src/components/TurkHit/index.js +++ b/src/components/TurkHit/index.js @@ -15,6 +15,20 @@ class TurkHit extends Component { }; } + renderDefinitions = () => ( +
+

Mechanical Turk

+
Definitions
+

+ Amazon Mechanical Turk is a marketplace for workers to complete Human + Intelligence Tasks or HITs. +
+ We will be creating HITs for workers to measure the dimensions of a building. + HITs have 6 days to be completed from date of creation. +

+
+ ); + renderDownloadLink = () => { const buildings = this.props.hit.boxBuildingList; /* eslint-disable jsx-a11y/href-no-hash*/ @@ -66,12 +80,15 @@ class TurkHit extends Component { let mainContent =
; if (hit.loading) { - return

Loading...

; + return ( +
+ {this.renderDefinitions()} +

Loading...

+
+ ); } else if (hit.error && hit.error.response.status !== 404) { return ( -
+
Retrieving HIT error. Response message: {hit.error.message}
); @@ -107,17 +124,9 @@ class TurkHit extends Component { Failed to create HIT. Response message: {hit.create.error.message}
-

Mechanical Turk

-
Definitions
-

- Amazon Mechanical Turk is a marketplace for workers to complete Human - Intelligence Tasks or HITs. -
- We will be creating HITs for workers to measure the dimensions of a building. - HITs have 6 days to be completed from date of creation. -

- + {this.renderDefinitions()} {mainContent} +
); } -- GitLab From 1bc7355bca87a72f828e0e536555153a584592d1 Mon Sep 17 00:00:00 2001 From: Alessandro DiMarco Date: Thu, 2 Feb 2017 13:50:32 -0500 Subject: [PATCH 6/7] Add confirmation box when creating new HIT --- src/components/TurkHit/index.js | 8 ++++---- src/containers/Dimensions/index.js | 10 +++++++++- 2 files changed, 13 insertions(+), 5 deletions(-) diff --git a/src/components/TurkHit/index.js b/src/components/TurkHit/index.js index 80b75e4a..d5059b5a 100644 --- a/src/components/TurkHit/index.js +++ b/src/components/TurkHit/index.js @@ -31,7 +31,7 @@ class TurkHit extends Component { renderDownloadLink = () => { const buildings = this.props.hit.boxBuildingList; - /* eslint-disable jsx-a11y/href-no-hash*/ + /* eslint-disable jsx-a11y/href-no-hash */ return ( { - const { createHit, address, building_id } = this.props; + const { createHitConfirmation, address, building_id } = this.props; return (