diff --git a/src/components/BuildingOverview/index.js b/src/components/BuildingOverview/index.js index df9a359b83a4829e371ece6eb3436ab967f65144..57ecd52a9bfc8c417421e9e7c7eed42e8880e7a6 100644 --- a/src/components/BuildingOverview/index.js +++ b/src/components/BuildingOverview/index.js @@ -1,6 +1,11 @@ import React, { Component, PropTypes } from 'react'; import documentsPropType from '../../containers/Documents/propTypes'; import DocumentCardViewer from '../../components/DocumentCardViewer'; +import ProjectCard from '../ProjectCard'; +import { + completeProjectPropTypes, + completeOverviewPropTypes, +} from '../../containers/Building/propTypes'; import './styles.css'; @@ -15,31 +20,47 @@ export default class BuildingOverview extends Component { }; } - render() { - const { building } = this.props; + renderProjects = () => { + const projectList = this.props.projects.list; + if (projectList.length <= 0) { + return ( +

None

+ ); + } + + return projectList.map(project => ( + + )); + } + render() { return ( -
-
-

- {building.address} -

-
    -
  • Zipcode: {building.zipcode}
  • -
  • BBL: {building.bbl}
  • -
  • Building ID: {building.building_id}
  • -
  • Lot ID: {building.lot_id}
  • -
  • Borough: {building.borough}
  • -
+
+
+
+
+
Projects
+ {this.renderProjects()} +
+
+ +
+ +
+
+
+
-
); } @@ -47,14 +68,8 @@ export default class BuildingOverview extends Component { BuildingOverview.propTypes = { buildingId: PropTypes.string, - building: PropTypes.shape({ - address: PropTypes.string, - bbl: PropTypes.number, - building_id: PropTypes.number, - lot_id: PropTypes.number, - borough: PropTypes.string, - zipcode: PropTypes.number, - }), + building: completeOverviewPropTypes, + projects: completeProjectPropTypes, documents: documentsPropType, getDocuments: PropTypes.func, uploadDocument: PropTypes.func, diff --git a/src/components/BuildingOverview/styles.css b/src/components/BuildingOverview/styles.css index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..eb9fe8270318098633f4bdc5e4836e74121d11b1 100644 --- a/src/components/BuildingOverview/styles.css +++ b/src/components/BuildingOverview/styles.css @@ -0,0 +1,3 @@ +.column { + flex: 0.33; +} diff --git a/src/components/DocumentCardViewer/index.js b/src/components/DocumentCardViewer/index.js index a17ec11a2ed8c68adf5544207491d83aafa0de18..73a605e5ca5d39e8bd2093547f9fba202e7f0acf 100644 --- a/src/components/DocumentCardViewer/index.js +++ b/src/components/DocumentCardViewer/index.js @@ -87,7 +87,7 @@ export default class DocumentCardViewer extends Component { render() { const { error, uploadError } = this.props.documents; return ( -
+
{ + return ( +
+ Coming Soon! +
+ {buildingId} +
+ ); +}; + +Project.propTypes = { + buildingId: PropTypes.string, +}; + +export default Project; diff --git a/src/components/Project/styles.css b/src/components/Project/styles.css new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/src/components/ProjectCard/index.js b/src/components/ProjectCard/index.js new file mode 100644 index 0000000000000000000000000000000000000000..317c3e960f35b405a03b43915ac01376be500582 --- /dev/null +++ b/src/components/ProjectCard/index.js @@ -0,0 +1,35 @@ +import React, { PropTypes } from 'react'; +import { Link, browserHistory } from 'react-router'; +import { projectPropTypes } from '../../containers/Building/propTypes'; + +/* eslint-disable jsx-a11y/href-no-hash */ +const ProjectCard = (props) => { + const { project } = props; + let date = ''; + if (project.updated !== null) { + date = `Last modified on ${new Date(project.updated)}`; + } else { + date = `Created on ${new Date(project.created)}`; + } + + return ( +
+ + { project.state } + { project.name } + + { project.program_type } + {date} + +
+ ); +}; + +ProjectCard.propTypes = { + project: PropTypes.shape(projectPropTypes), +}; + +export default ProjectCard; diff --git a/src/components/ProjectCard/styles.css b/src/components/ProjectCard/styles.css new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/src/components/SideBarDetail/index.js b/src/components/SideBarDetail/index.js index bbe99903095cd2325b319039d7d43dbcb0e37766..af5cf3e236af7922b2e70435161b06cdd5b3e115 100644 --- a/src/components/SideBarDetail/index.js +++ b/src/components/SideBarDetail/index.js @@ -10,7 +10,6 @@ const isActive = (url) => { return ''; }; -/* eslint-disable */ export default function SideBarDetail({ building, children }) { const rootURL = `/buildings/${building.building_id}`; return ( @@ -50,9 +49,10 @@ export default function SideBarDetail({ building, children }) {
    -
  • - - {/* All of this SVGs will need to replaced with InlineSVG, an external library. Then we can link to files */} +
  • + + {/* All of this SVGs will need to replaced with InlineSVG, an external library. + Then we can link to files */} diff --git a/src/containers/Building/actions.js b/src/containers/Building/actions.js index 28840a22deeefdc87f5416101a489c6958eb0824..6b239eea865aa34e89ee10f9f184ffdda43491d1 100644 --- a/src/containers/Building/actions.js +++ b/src/containers/Building/actions.js @@ -3,15 +3,9 @@ import { LOAD_BUILDING_DETAIL, LOAD_BUILDING_DETAIL_SUCCEES, LOAD_BUILDING_DETAIL_ERROR, - LOAD_HIT, - LOAD_HIT_SUCCESS, - LOAD_HIT_ERROR, - CREATE_HIT, - CREATE_HIT_SUCCESS, - CREATE_HIT_ERROR, - DECIDE_HIT, - DECIDE_HIT_SUCCESS, - DECIDE_HIT_ERROR, + LOAD_PROJECTS, + LOAD_PROJECTS_SUCCESS, + LOAD_PROJECTS_ERROR, } from './constants'; /** @@ -21,10 +15,10 @@ import { * @returns {object} An action object with a type of LOAD_BUILDING_DETAIL * passing the building detail */ -export function loadBuildingDetail(buildingID) { +export function loadBuildingDetail(buildingId) { return { type: LOAD_BUILDING_DETAIL, - payload: buildingID, + payload: buildingId, }; } @@ -56,127 +50,23 @@ export function buildingDetailLoadingError(error) { }; } -/** - * Load status of HIT - * TODO The HIT status should be loaded from the building detail route - * - * @param {string} buildingId The current buildingID - * @returns {object} An action object with a type of - * LOAD_HIT passing the building_id - */ -export function loadHit(buildingId) { - return { - type: LOAD_HIT, - payload: buildingId, - }; -} - -/** - * Dispatched when the hit status was successfully loaded - * - * @param {string} hitStatus The HIT status - * @returns {object} An action object with a type of - * LOAD_HIT_SUCCESS passing the hit status - */ -export function hitLoaded(hitStatus) { - return { - type: LOAD_HIT_SUCCESS, - payload: hitStatus, - }; -} - -/** - * Dispatched when loading the hit status fails - * - * @param {object} error The error - * @returns {object} An action object with a type of - * LOAD_HIT passing the hit status - */ -export function hitLoadingError(error) { - return { - type: LOAD_HIT_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, - }; -} - -/** - * 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_SUCCESS - */ -export function createHitSuccess(hitData) { - return { - type: CREATE_HIT_SUCCESS, - 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, - }; -} - -/** - * Dispatched when user selects approve or reject HIT - * - * @param {int} buildingId Id of building - * @param {int} decision 1 or 0 representing approve or reject - * @returns {object} An action object with a type of DECIDE_HIT - * passing the decision of the user - */ -export function hitDecision(buildingId, decision) { +export function loadProjects(buildingId) { return { - type: DECIDE_HIT, + type: LOAD_PROJECTS, buildingId, - decision, }; } -/** - * Dispatched when the hit was successfully approved or disapproved - * - * @returns {object} - */ -export function hitDecisionSuccess(data) { +export function projectsLoaded(projects) { return { - type: DECIDE_HIT_SUCCESS, - payload: data, + type: LOAD_PROJECTS_SUCCESS, + payload: projects.data, }; } -/** - * Dispatched when there is a error approving or rejecting a HIT - * - * @param error The error - * @returns {object} An action object with a type of DECIDE_HIT_ERROR - * passing the error - */ -export function hitDecisionError(error) { +export function projectsLoadingError(error) { return { - type: DECIDE_HIT_ERROR, + type: LOAD_PROJECTS_ERROR, error, }; } diff --git a/src/containers/Building/constants.js b/src/containers/Building/constants.js index 25650cab4a7ecc0110cac9202331cfc7ff8c005f..9ec88499b7cd84a87a3ecaab2e69fa5172b96cd8 100644 --- a/src/containers/Building/constants.js +++ b/src/containers/Building/constants.js @@ -1,3 +1,6 @@ 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 LOAD_PROJECTS = 'LOAD_PROJECTS'; +export const LOAD_PROJECTS_SUCCESS = 'LOAD_PROJECTS_SUCCESS'; +export const LOAD_PROJECTS_ERROR = 'LOAD_PROJECTS_ERROR'; diff --git a/src/containers/Building/index.js b/src/containers/Building/index.js index 976fcf5b55cce9614a14615dae4d7862064d89ef..4f9f6bb024d6a5b1487887a9da96a72e9a536e09 100644 --- a/src/containers/Building/index.js +++ b/src/containers/Building/index.js @@ -2,28 +2,31 @@ import React, { Component, PropTypes } from 'react'; import { connect } from 'react-redux'; import { bindActionCreators } from 'redux'; -import buildingDetailPropTypes from './propTypes'; -import { loadBuildingDetail } from './actions'; +import { completeOverviewPropTypes, completeProjectPropTypes } from './propTypes'; +import { loadBuildingDetail, loadProjects } from './actions'; import documentsPropType from '../Documents/propTypes'; import { loadDocuments, uploadDocument } from '../Documents/actions'; import SideBarDetail from '../../components/SideBarDetail'; +import ErrorAlert from '../../components/ErrorAlert'; import './styles.css'; class Building extends Component { componentDidMount() { this.props.loadBuildingDetail(this.props.buildingId); + this.props.loadProjects(this.props.buildingId); } render() { - const { error, loading, address } = this.props.buildingDetail.overview; + const { error, loading, address } = this.props.overview; let mainContent =
    ; if (this.props.children && !error && !loading && address !== undefined) { mainContent = React.cloneElement(this.props.children, { buildingId: this.props.buildingId, - building: this.props.buildingDetail.overview, + building: this.props.overview, + projects: this.props.projects, documents: this.props.documents, getDocuments: this.props.loadDocuments, uploadDocument: this.props.uploadDocument, @@ -32,14 +35,12 @@ class Building extends Component { return (
    - -
    -
    - {`There was a error retrieving the building | ${error.message}`} -
    + +
    + { mainContent }
    @@ -49,9 +50,11 @@ class Building extends Component { Building.propTypes = { children: PropTypes.element, - buildingDetail: buildingDetailPropTypes, buildingId: PropTypes.string, + overview: completeOverviewPropTypes, loadBuildingDetail: PropTypes.func, + projects: completeProjectPropTypes, + loadProjects: PropTypes.func, documents: documentsPropType, loadDocuments: PropTypes.func, uploadDocument: PropTypes.func, @@ -60,13 +63,19 @@ Building.propTypes = { function mapDispatchToProps(dispatch) { return bindActionCreators({ loadBuildingDetail, + loadProjects, loadDocuments, uploadDocument, }, dispatch); } function mapStateToProps({ buildingDetail, documents }) { - return { buildingDetail, documents }; + const { overview, projects } = buildingDetail; + return { + overview, + projects, + documents, + }; } export default connect(mapStateToProps, mapDispatchToProps)(Building); diff --git a/src/containers/Building/propTypes.js b/src/containers/Building/propTypes.js index 98e5b02bb340b77bfa4446082bfcfd91c51c2314..1ccfa3fd472510455cf2a3213d9785cad3685158 100644 --- a/src/containers/Building/propTypes.js +++ b/src/containers/Building/propTypes.js @@ -1,21 +1,49 @@ import { PropTypes } from 'react'; +const { string, number, bool, shape, arrayOf, oneOfType } = PropTypes; + +export const loadErrorPropTypes = { + loading: bool, + error: oneOfType([ + bool, + string, + ]), +}; + +export const overviewPropTypes = { + address: string, + bbl: number, + bin_id: number, + building_id: number, + lot_id: number, + borough: string, + zipcode: number, +}; + +export const projectPropTypes = { + building_id: string, + client_id: number, + created: string, + id: number, + name: string, + program_type: string, + sales_force_id: string, + slug: string, + state: string, + updated: string, +}; + +export const completeOverviewPropTypes = shape({ + ...overviewPropTypes, + ...loadErrorPropTypes, +}); + +export const completeProjectPropTypes = shape({ + list: arrayOf(shape(projectPropTypes)), + ...loadErrorPropTypes, +}); + export default PropTypes.shape({ - overview: PropTypes.shape({ - address: PropTypes.string, - bbl: PropTypes.number, - bin_id: PropTypes.number, - building_id: PropTypes.number, - lot_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, - }), + completeOverviewPropTypes, + completeProjectPropTypes, }); diff --git a/src/containers/Building/reducer.js b/src/containers/Building/reducer.js index 3b9ef359a1225266fa0ce1ff42f036baa880f6ca..b9c73ebe071fabbb6a5cfddb8297da91b5f1f555 100644 --- a/src/containers/Building/reducer.js +++ b/src/containers/Building/reducer.js @@ -2,6 +2,9 @@ import { LOAD_BUILDING_DETAIL, LOAD_BUILDING_DETAIL_SUCCEES, LOAD_BUILDING_DETAIL_ERROR, + LOAD_PROJECTS, + LOAD_PROJECTS_SUCCESS, + LOAD_PROJECTS_ERROR, } from './constants'; const initState = { @@ -9,6 +12,11 @@ const initState = { loading: false, error: false, }, + projects: { + projects: [], + loading: false, + error: false, + }, }; export default function (state = initState, action) { @@ -43,6 +51,34 @@ export default function (state = initState, action) { }, }; + case LOAD_PROJECTS: + return { + ...state, + projects: { + loading: true, + error: false, + }, + }; + + case LOAD_PROJECTS_SUCCESS: + return { + ...state, + projects: { + list: action.payload, + loading: false, + error: false, + }, + }; + + case LOAD_PROJECTS_ERROR: + return { + ...state, + projects: { + loading: false, + error: false, + }, + }; + default: return state; } diff --git a/src/containers/Building/sagas.js b/src/containers/Building/sagas.js index edab0fc02fa8c9d4169659616fe16511f5c29f6b..5e5852a98ba517b76a6ca10593afeb2cdcf7aed8 100644 --- a/src/containers/Building/sagas.js +++ b/src/containers/Building/sagas.js @@ -1,14 +1,16 @@ -import { call, put, takeEvery } from 'redux-saga/effects'; +import { call, put, takeEvery, takeLatest } from 'redux-saga/effects'; import request from '../../utils/request'; import { getHeaders, buildingsURL } from '../../utils/restServices'; import { LOAD_BUILDING_DETAIL, + LOAD_PROJECTS, } from './constants'; import { buildingDetailLoaded, buildingDetailLoadingError, + projectsLoaded, } from './actions'; /** @@ -18,7 +20,7 @@ import { */ function* getBuildingDetail(action) { const buildingId = action.payload; - const data = yield call( + const res = yield call( request, `${buildingsURL}${buildingId}`, { method: 'GET', @@ -26,19 +28,30 @@ function* getBuildingDetail(action) { } ); - if (!data.err) { - yield put(buildingDetailLoaded(data)); + if (!res.err) { + yield put(buildingDetailLoaded(res)); } else { - yield put(buildingDetailLoadingError(data.err)); + yield put(buildingDetailLoadingError(res.err)); } } +function* getProjects(action) { + const buildingId = action.buildingId; + const res = { + buildingId, + data: [], + }; + + yield put(projectsLoaded(res)); +} + /** - * Watches for LOAD_BUILDING_DETAIL & CREATE_HIT actions and calls + * Watches for LOAD_BUILDING_DETAIL & LOAD_PROJECTS actions and calls * the appropriate handler function. */ -function* buildingDetailWatcher() { +function* buildingWatcher() { yield takeEvery(LOAD_BUILDING_DETAIL, getBuildingDetail); + yield takeLatest(LOAD_PROJECTS, getProjects); } -export default buildingDetailWatcher; +export default buildingWatcher; diff --git a/src/containers/Documents/reducer.js b/src/containers/Documents/reducer.js index 0829c6a5d6915a611e0de237813b6956c1f43ba2..6768abd4ba126be67231deecf2a25fa74fbc3eb8 100644 --- a/src/containers/Documents/reducer.js +++ b/src/containers/Documents/reducer.js @@ -14,6 +14,7 @@ const initState = { uploadError: false, files: { building: [], // Root Directory + project: [], utilityBills: [], // Utility_Bills buildingDimensions: [], // Building_Dimensions }, diff --git a/src/routes.js b/src/routes.js index 4211ce3fed569331714c79b38e7e33e6e5f2903f..6313f7d4f00e4ab3448f762c5c1bbf8334260f68 100644 --- a/src/routes.js +++ b/src/routes.js @@ -8,6 +8,7 @@ import NotFound from './screens/NotFound'; import HomePage from './screens/HomePage'; import DetailPage from './screens/DetailPage'; import BuildingOverview from './components/BuildingOverview'; +import Project from './components/Project'; import Dimensions from './containers/Dimensions'; import Utilities from './components/Utilities'; @@ -18,8 +19,8 @@ export default ( - - + + diff --git a/src/sagas.js b/src/sagas.js index 79cf9f7399e11c0abf645af3a5aecb49c8228c8e..dd26ab9070b37ff8de309b9aa54d1600064d243f 100644 --- a/src/sagas.js +++ b/src/sagas.js @@ -1,6 +1,6 @@ -import buildingsSearchSaga from './containers/SearchBar/sagas'; import buildingSaga from './containers/Building/sagas'; import dimensionsSaga from './containers/Dimensions/sagas'; +import buildingsSearchSaga from './containers/SearchBar/sagas'; import documentsSaga from './containers/Documents/sagas'; export default function* rootSaga() {