diff --git a/src/components/main/body/Body.jsx b/src/components/main/body/Body.jsx index 2b0e2bdfa..73dc15365 100644 --- a/src/components/main/body/Body.jsx +++ b/src/components/main/body/Body.jsx @@ -1,19 +1,40 @@ import React from 'react'; +import { connect } from 'react-redux'; +import PropTypes from 'proptypes'; import Visualizations from '@components/Visualizations'; import Loader from '@components/common/Loader'; +import Modal from '@components/common/Modal'; import Menu from '../menu/Menu'; import PinMap from '../../PinMap/PinMap'; +import DataRequestError from './DataRequestError'; -const Body = () => ( +const Body = ({ + openErrorModal, + error, +}) => (
+ } + />
); -export default Body; +Body.propTypes = { + error: PropTypes.shape({}).isRequired, + openErrorModal: PropTypes.bool.isRequired, +}; + +const mapStateToProps = state => ({ + error: state.data.error, + openErrorModal: state.ui.error.isOpen, +}); + +export default connect(mapStateToProps, null)(Body); diff --git a/src/components/main/body/DataRequestError.jsx b/src/components/main/body/DataRequestError.jsx new file mode 100644 index 000000000..4f0c956ba --- /dev/null +++ b/src/components/main/body/DataRequestError.jsx @@ -0,0 +1,48 @@ +import React from 'react'; +import { connect } from 'react-redux'; +import propTypes from 'proptypes'; + +import { setErrorModal } from '@reducers/ui'; + +import Icon from '@components/common/Icon'; +import Button from '@components/common/Button'; + +const DataRequestError = ({ closeModal }) => ( +
+
+ +
+
+

+ Something went wrong +

+
+

+ We failed to retrieve data for this request; please try again. +

+
+
+
+
+); + +DataRequestError.propTypes = { + closeModal: propTypes.func.isRequired, +}; + +const mapDispatchToProps = dispatch => ({ + closeModal: () => dispatch(setErrorModal(false)), +}); + +export default connect(null, mapDispatchToProps)(DataRequestError); diff --git a/src/components/main/header/Header.jsx b/src/components/main/header/Header.jsx index 6b70222a1..d760cb304 100644 --- a/src/components/main/header/Header.jsx +++ b/src/components/main/header/Header.jsx @@ -1,11 +1,8 @@ import React from 'react'; -import propTypes from 'proptypes'; -import { connect } from 'react-redux'; + import COLORS from '../../../styles/COLORS'; -const Header = ({ - data, -}) => { +const Header = () => { const cta2Style = { color: COLORS.BRAND.CTA2, fontWeight: 'bold', @@ -22,86 +19,46 @@ const Header = ({ }; return ( - <> -
-
-
-

311

-

Data

-
+
+
+
+

311

+

Data

+
-
- {/* Errors */} - {data.error && ( - - )} - +
+ +
); }; -const mapStateToProps = state => ({ - data: state.data, -}); - -Header.propTypes = { - data: propTypes.shape({ - error: propTypes.shape({ - message: propTypes.string, - }), - }), -}; - -Header.defaultProps = { - data: undefined, -}; - -export default connect(mapStateToProps, null)(Header); +export default Header; diff --git a/src/redux/reducers/data.js b/src/redux/reducers/data.js index a32b54104..f5b5952f5 100644 --- a/src/redux/reducers/data.js +++ b/src/redux/reducers/data.js @@ -35,31 +35,26 @@ export default (state = initialState, action) => { ...state, isLoading: true, }; - case types.GET_DATA_SUCCESS: { - const { - lastUpdated, - pins, - counts, - frequency, - timeToClose, - } = action.payload; - + case types.GET_DATA_SUCCESS: return { ...state, error: null, isLoading: false, - lastUpdated, - pins, - counts, - frequency, - timeToClose, + ...action.payload, }; - } case types.GET_DATA_FAILURE: { - const { error } = action.payload; + const { + response: { status }, + message, + } = action.payload; + return { ...state, - error, + error: { + code: status, + message, + error: action.payload, + }, isLoading: false, }; } diff --git a/src/redux/reducers/ui.js b/src/redux/reducers/ui.js index a6ebe75e5..c3d247cba 100644 --- a/src/redux/reducers/ui.js +++ b/src/redux/reducers/ui.js @@ -3,6 +3,7 @@ import { MENU_TABS } from '@components/common/CONSTANTS'; const types = { TOGGLE_MENU: 'TOGGLE_MENU', SET_MENU_TAB: 'SET_MENU_TAB', + SET_ERROR_MODAL: 'SET_ERROR_MODAL', }; export const toggleMenu = () => ({ @@ -14,11 +15,19 @@ export const setMenuTab = tab => ({ payload: tab, }); +export const setErrorModal = isOpen => ({ + type: types.SET_ERROR_MODAL, + payload: isOpen, +}); + const initialState = { menu: { isOpen: true, activeTab: MENU_TABS.MAP, }, + error: { + isOpen: false, + }, }; export default (state = initialState, action) => { @@ -31,6 +40,14 @@ export default (state = initialState, action) => { isOpen: !state.menu.isOpen, }, }; + case types.SET_ERROR_MODAL: + return { + ...state, + error: { + ...state.error, + isOpen: action.payload, + }, + }; case types.SET_MENU_TAB: return { ...state, diff --git a/src/redux/rootSaga.js b/src/redux/rootSaga.js index 50f6bdf00..0439bafe7 100644 --- a/src/redux/rootSaga.js +++ b/src/redux/rootSaga.js @@ -6,12 +6,17 @@ import { select, all, } from 'redux-saga/effects'; + import { types, getDataSuccess, getDataFailure, } from './reducers/data'; +import { + setErrorModal, +} from './reducers/ui'; + /* /////////// INDIVIDUAL API CALLS /////////// */ const BASE_URL = process.env.DB_URL; @@ -102,6 +107,7 @@ function* getData() { yield put(getDataSuccess(data)); } catch (e) { yield put(getDataFailure(e)); + yield put(setErrorModal(true)); } } diff --git a/src/styles/main/_body.scss b/src/styles/main/_body.scss new file mode 100644 index 000000000..9459c5f4a --- /dev/null +++ b/src/styles/main/_body.scss @@ -0,0 +1,6 @@ +.data-request-error { + border-radius: 5px; + background: rgba(153, 153, 153, 0.7); + padding: 10px 35px; + margin: 0 10em; +} \ No newline at end of file diff --git a/src/styles/styles.scss b/src/styles/styles.scss index 1589a2a8d..c2d88bd48 100644 --- a/src/styles/styles.scss +++ b/src/styles/styles.scss @@ -9,3 +9,4 @@ @import './main/tooltip'; @import './main/visualizations'; @import './main/loader'; +@import './main/body';