diff --git a/src/components/Ashp/ApartmentTypeDropdown.js b/src/components/Ashp/ApartmentTypeDropdown.js new file mode 100644 index 0000000000000000000000000000000000000000..564bc513de7b9da4beb42202173bc4b7d6e496ba --- /dev/null +++ b/src/components/Ashp/ApartmentTypeDropdown.js @@ -0,0 +1,56 @@ +import React, { Component } from 'react'; +import PropTypes from 'prop-types'; +import { + Input, +} from 'reactstrap'; + + +class ApartmentTypeDropdown extends Component { + constructor(props) { + super(props); + this.state = { + numOfApartment: props.numOfApartment, + }; + } + + handleInputChange = (event) => { + const val = event.target.value; + // const name = event.target.name; + this.setState({ + numOfApartment: val, + }, () => { + this.props.onChange(this.props.inputName, this.state.numOfApartment); + console.log('changed!'); // eslint-disable-line + }); + } + + render() { + return ( +
+
+ this.handleInputChange(e)} + > + + + + + + + +
+
+ ); + } +} + +ApartmentTypeDropdown.propTypes = { + inputName: PropTypes.string, + numOfApartment: PropTypes.number, + onChange: PropTypes.func, +}; + +export default ApartmentTypeDropdown; diff --git a/src/components/Ashp/BuildingInfo.js b/src/components/Ashp/BuildingInfo.js new file mode 100644 index 0000000000000000000000000000000000000000..7651e7f6e8632be166f5ed349886ae89e0f6fc55 --- /dev/null +++ b/src/components/Ashp/BuildingInfo.js @@ -0,0 +1,259 @@ +import React, { Component } from 'react'; +import PropTypes from 'prop-types'; +import { Table, Input } from 'reactstrap'; +import ApartmentTypeDropdown from './ApartmentTypeDropdown'; + +class BuildingInfo extends Component { + constructor(props) { + super(props); + this.state = { + form: { + firstName: props.firstName, + lastName: props.lastName, + email: props.email, + phone: props.phone, + numOfStudio: props.numOfStudio, + numOfOneBedroom: props.numOfOneBedroom, + numOfTwoBedroom: props.numOfTwoBedroom, + numOfThreeBedroom: props.numOfThreeBedroom, + numOfFourBedroom: props.numOfFourBedroom, + other: props.other, + numOfOther: props.numOfOther, + address: props.address, + }, + }; + } + + handleInputChange = (event) => { + const val = event.target.value; + const name = event.target.name; + this.setState({ + form: { + ...this.state.form, + [name]: val, + }, + }, () => { + this.props.onChangeEvent('buildingInfo', this.state.form); + console.log('changed!'); // eslint-disable-line + }); + } + + handleInputChangeSubComponent = (name, val) => { + this.setState({ + form: { + ...this.state.form, + [name]: val, + }, + }, () => { + this.props.onChangeEvent('buildingInfo', this.state.form); + console.log('changed!'); // eslint-disable-line + }); + } + + render() { + const address = this.props.address.map(addressItem => { + return ( +
+ {addressItem} +
+ ); + }); + return ( +
+ + + + + + + +
+ Building Intake +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ Full Name + + this.handleInputChange(e)} + /> + + this.handleInputChange(e)} + /> +
+ Email + + this.handleInputChange(e)} + /> +
+ Phone No. + + this.handleInputChange(e)} + /> +
+ Address + + {address} +
+ Apartment Types Breakdown + +
+ Number of Studio +
+ +
+
+ Number of One Bedroom Apartment +
+ +
+ {' '} + +
+ Number of Two Bedroom Apartment +
+ +
+
+ Number of Three Bedroom Apartment +
+ +
+ {' '} + +
+ Number of Four Bedroom Apartment +
+ +
+
+ Other +
+
+
+ this.handleInputChange(e)} + /> +
+
+ this.handleInputChange(e)} + > + + + + + + + + +
+
+
+
+
+
+ ); + } +} + +BuildingInfo.propTypes = { + tableHeader: PropTypes.objectOf, + labelStyle: PropTypes.objectOf, + address: PropTypes.arrayOf, + firstName: PropTypes.string, + lastName: PropTypes.string, + email: PropTypes.string, + phone: PropTypes.string, + numOfStudio: PropTypes.number, + numOfOneBedroom: PropTypes.number, + numOfTwoBedroom: PropTypes.number, + numOfThreeBedroom: PropTypes.number, + numOfFourBedroom: PropTypes.number, + other: PropTypes.string, + numOfOther: PropTypes.number, + onChangeEvent: PropTypes.func, +}; + +export default BuildingInfo; diff --git a/src/components/Ashp/FinancialInitial.js b/src/components/Ashp/FinancialInitial.js new file mode 100644 index 0000000000000000000000000000000000000000..4fc5b3a550b80916f1a5ea2bbd7c423ff17b7bf8 --- /dev/null +++ b/src/components/Ashp/FinancialInitial.js @@ -0,0 +1,165 @@ +import React, { Component } from 'react'; +import PropTypes from 'prop-types'; +import { Table, Input } from 'reactstrap'; + + +class FinanacialInitial extends Component { + constructor(props) { + super(props); + this.state = { + form: { + bankruptyPast: props.bankruptyPast, + currentOnEnergyBills: props.currentOnEnergyBills, + currentMortgageBalance: props.currentMortgageBalance, + profitable: props.profitable, + budgetForDownPayment: props.budgetForDownPayment, + }, + }; + } + + handleInputChange = (event) => { + const val = event.target.value; + const name = event.target.name; + this.setState({ + form: { + ...this.state.form, + [name]: val, + }, + }, () => { + this.props.onChangeEvent('financialInitials', this.state.form); + console.log('changed!'); // eslint-disable-line + }); + } + + render() { + return ( +
+ + + + + + + +
+ Financial Initial Go/NoGo +
+
+ + + + + + + + + + + + + + + + + + + +
+ Any bankruptcy in the past three years? + + this.handleInputChange(e)} + > + + + + + + + Current on energy bills? + + this.handleInputChange(e)} + > + + + + + + + + +
+ What is the current outstanding balance of the mortgage? + + this.handleInputChange(e)} + > + + + + + + + + + Is your building currently profitable? + + this.handleInputChange(e)} + > + + + + + +
+ What is your current budget for a down payment? + + this.handleInputChange(e)} + > + + + + + + {' '} + + {' '} +
+
+
+
+ ); + } +} + +FinanacialInitial.propTypes = { + tableHeader: PropTypes.objectOf, + bankruptyPast: PropTypes.string, + currentOnEnergyBills: PropTypes.string, + currentMortgageBalance: PropTypes.string, + profitable: PropTypes.string, + budgetForDownPayment: PropTypes.string, + onChangeEvent: PropTypes.func, +}; + +export default FinanacialInitial; diff --git a/src/components/Ashp/RemoteSurvey.js b/src/components/Ashp/RemoteSurvey.js new file mode 100644 index 0000000000000000000000000000000000000000..dadcdc3b36f26dafa6017cfb6a80540bddc91e37 --- /dev/null +++ b/src/components/Ashp/RemoteSurvey.js @@ -0,0 +1,391 @@ +import React, { Component } from 'react'; +import PropTypes from 'prop-types'; +import { Table, Input } from 'reactstrap'; + + +class RemoteSurvey extends Component { + constructor(props) { + super(props); + this.state = { + form: { + heatingSystem: props.heatingSystem, + heatingFuelSource: props.heatingFuelSource, + DHWSameBoiler: props.DHWSameBoiler, + AgeOfHeatGenerateSystem: props.AgeOfHeatGenerateSystem, + planToReplaceHS: props.planToReplaceHS, + hallwaysHeated: props.hallwaysHeated, + basementHeated: props.basementHeated, + stairwellsHeated: props.stairwellsHeated, + floorsHeated: props.floorsHeated, + accessibleByStairwell: props.accessibleByStairwell, + backFacadeAttached: props.backFacadeAttached, + exteriorWallsAttached: props.exteriorWallsAttached, + tenantComplaintsComfort: props.tenantComplaintsComfort, + tenantComplaintsWindows: props.tenantComplaintsWindows, + meteredForElectricity: props.meteredForElectricity, + }, + }; + } + + handleInputChange = (event) => { + const val = event.target.value; + const name = event.target.name; + this.setState({ + form: { + ...this.state.form, + [name]: val, + }, + }, () => { + this.props.onChangeEvent('remoteSurvey', this.state.form); + console.log('changed!'); // eslint-disable-line + }); + } + + render() { + return ( +
+ + + + + + + +
+ Remote Survey +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ Heating System + + this.handleInputChange(e)} + > + + + + + + + + + Heating Fuel Source + + this.handleInputChange(e)} + > + + + + + + + +
+ Is Domestic Hot Water (DHW) and heat prepared by the same boiler? + + this.handleInputChange(e)} + > + + + + + + + Age of heat generating system? + + this.handleInputChange(e)} + > + + + + + + + +
+ When do you plan to replace the heating system? + + this.handleInputChange(e)} + > + + + + + + + + {' '} + + {' '} +
+ Are the following building areas heated? + +
+ Hallways +
+
+
+ this.handleInputChange(e)} + > + + + + + +
+
+
+
+ Basement +
+
+
+ this.handleInputChange(e)} + > + + + + + +
+
+
+ {' '} +
+ {' '} + +
+ Stairwells +
+
+
+ this.handleInputChange(e)} + > + + + + + +
+
+
+
+ Floors +
+
+
+ this.handleInputChange(e)} + > + + + + + +
+
+
+ {' '} +
+ Is the roof flat and easily accessible by a stairwell? + + this.handleInputChange(e)} + > + + + + + + + Is the back facade of your building attached to + an adjacent building or structure? + + this.handleInputChange(e)} + > + + + + + +
+ Are the exterior walls of your building attached to + an adjacent building or structure? + + this.handleInputChange(e)} + > + + + + + + + + Any tenant complaints about comfort issues? (Temperature) + + this.handleInputChange(e)} + > + + + + + + +
+ Any tenant complaints about drafty windows? + + this.handleInputChange(e)} + > + + + + + + + + Is the building directly metered for electricity + (tenants pay ConEd bills)? + + this.handleInputChange(e)} + > + + + + + +
+
+
+
+ ); + } +} + +RemoteSurvey.propTypes = { + tableHeader: PropTypes.objectOf, + labelStyle: PropTypes.objectOf, + heatingSystem: PropTypes.string, + heatingFuelSource: PropTypes.string, + DHWSameBoiler: PropTypes.string, + planToReplaceHS: PropTypes.string, + hallwaysHeated: PropTypes.string, + basementHeated: PropTypes.string, + stairwellsHeated: PropTypes.string, + floorsHeated: PropTypes.string, + accessibleByStairwell: PropTypes.string, + backFacadeAttached: PropTypes.string, + exteriorWallsAttached: PropTypes.string, + tenantComplaintsWindows: PropTypes.string, + tenantComplaintsComfort: PropTypes.string, + meteredForElectricity: PropTypes.string, + AgeOfHeatGenerateSystem: PropTypes.string, + onChangeEvent: PropTypes.func, +}; + +export default RemoteSurvey; diff --git a/src/components/Ashp/ReviewAnswers/BuildingInfo.js b/src/components/Ashp/ReviewAnswers/BuildingInfo.js new file mode 100644 index 0000000000000000000000000000000000000000..3c8efa1da16c16f3f35208f76e202cbede886ce2 --- /dev/null +++ b/src/components/Ashp/ReviewAnswers/BuildingInfo.js @@ -0,0 +1,139 @@ +import React from 'react'; +import PropTypes from 'prop-types'; +import { Table } from 'reactstrap'; + + +const BuildingInfo = (props) => { + const cellStyle = { + fontSize: '0.8em', + fontWeight: 'bold', + color: '#CCCCCC', + }; + const address = props.data.address.map(addressItem => { + return ( +
+ {addressItem} +
+ ); + }); + return ( +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ {props.title} +
+
+ First Name +
+
+ {props.upperFirstLetter(props.data.firstName)} +
+
+
+ Last Name +
+
+ {props.upperFirstLetter(props.data.lastName)} +
+
+
+ Email +
+
+ {props.data.email} +
+
+
+ Phone +
+
+ {props.data.phone} +
+
+
+ Address +
+
+ {address} +
+
+
+ Number of Studio +
+
+ {props.data.numOfStudio} +
+
+
+ Number of One Bedroom Apartment +
+
+ {props.data.numOfOneBedroom} +
+
+
+ Number of Two Bedroom Apartment +
+
+ {props.data.numOfTwoBedroom} +
+
+
+ Number of Three Bedroom Apartment +
+
+ {props.data.numOfThreeBedroom} +
+
+
+ Number of Four Bedroom Apartment +
+
+ {props.data.numOfFourBedroom} +
+
+
+ Other Apartment +
+
+ {props.data.other} {props.data.numOfOther} +
+
+
+ ); +}; + +BuildingInfo.propTypes = { + tableHeader: PropTypes.objectOf, + title: PropTypes.string, + data: PropTypes.objectOf, + upperFirstLetter: PropTypes.func, +}; + +export default BuildingInfo; diff --git a/src/components/Ashp/ReviewAnswers/FinancialInitial.js b/src/components/Ashp/ReviewAnswers/FinancialInitial.js new file mode 100644 index 0000000000000000000000000000000000000000..70bab548e59ec4b410ad9f1d4e061db0db3ec51a --- /dev/null +++ b/src/components/Ashp/ReviewAnswers/FinancialInitial.js @@ -0,0 +1,82 @@ +import React from 'react'; +import PropTypes from 'prop-types'; +import { Table } from 'reactstrap'; + + +const FinancialInitial = (props) => { + const cellStyle = { + fontSize: '0.8em', + fontWeight: 'bold', + color: '#BBBBBB', + }; + return ( +
+ + + + + + + + + + + + + + + + + + + +
+ {props.title} +
+
+ Any bankruptcy in the past three years? +
+
+ {props.upperFirstLetter(props.data.bankruptyPast)} +
+
+
+ Current on energy bills? +
+
+ {props.upperFirstLetter(props.data.currentOnEnergyBills)} +
+
+
+ What is the current outstanding balance of the mortgage? +
+
+ {props.upperFirstLetter(props.data.currentMortgageBalance)} +
+
+
+ Is your building currently profitable? +
+
+ {props.upperFirstLetter(props.data.profitable)} +
+
+
+ What is your current budget for a down payment? +
+
+ {props.upperFirstLetter(props.data.budgetForDownPayment)} +
+
+
+ ); +}; + +FinancialInitial.propTypes = { + tableHeader: PropTypes.objectOf, + title: PropTypes.string, + data: PropTypes.objectOf, + upperFirstLetter: PropTypes.func, +}; + +export default FinancialInitial; diff --git a/src/components/Ashp/ReviewAnswers/RemoteSurvey.js b/src/components/Ashp/ReviewAnswers/RemoteSurvey.js new file mode 100644 index 0000000000000000000000000000000000000000..c7bc75c80db9a4649b9b9e908ec6caf154340b14 --- /dev/null +++ b/src/components/Ashp/ReviewAnswers/RemoteSurvey.js @@ -0,0 +1,176 @@ +import React from 'react'; +import PropTypes from 'prop-types'; +import { Table } from 'reactstrap'; + + +const RemoteSurvey = (props) => { + const cellStyle = { + fontSize: '0.8em', + fontWeight: 'bold', + color: '#BBBBBB', + }; + return ( +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ {props.title} +
+
+ Heating System +
+
+ {props.upperFirstLetter(props.data.heatingSystem)} +
+
+
+ Heating Fuel Source +
+
+ {props.upperFirstLetter(props.data.heatingFuelSource)} +
+
+
+ Is Domestic Hot Water (DHW) and heat prepared by the same boiler? +
+
+ {props.upperFirstLetter(props.data.DHWSameBoiler)} +
+
+
+ Age of heat generating system? +
+
+ {props.upperFirstLetter(props.data.AgeOfHeatGenerateSystem)} +
+
+
+ When do you plan to replace the heating system? +
+
+ {props.upperFirstLetter(props.data.planToReplaceHS)} +
+
+
+ Hallways heated? +
+
+ {props.upperFirstLetter(props.data.hallwaysHeated)} +
+
+
+ Basement heated? +
+
+ {props.upperFirstLetter(props.data.basementHeated)} +
+
+
+ Stairwells heated? +
+
+ {props.upperFirstLetter(props.data.stairwellsHeated)} +
+
+
+ Floors heated? +
+
+ {props.upperFirstLetter(props.data.floorsHeated)} +
+
+
+ Is the roof flat and easily accessible by a stairwell? +
+
+ {props.upperFirstLetter(props.data.accessibleByStairwell)} +
+
+
+ Is the back facade of your building attached to an adjacent building or structure? +
+
+ {props.upperFirstLetter(props.data.backFacadeAttached)} +
+
+
+ Are the exterior walls of your building attached to an adjacent building or structure? +
+
+ {props.upperFirstLetter(props.data.exteriorWallsAttached)} +
+
+
+ Any tenant complaints about comfort issues? (Temperature) +
+
+ {props.upperFirstLetter(props.data.tenantComplaintsComfort)} +
+
+
+ Any tenant complaints about drafty windows? +
+
+ {props.upperFirstLetter(props.data.tenantComplaintsWindows)} +
+
+
+ Is the building directly metered for electricity (tenants pay ConEd bills)? +
+
+ {props.upperFirstLetter(props.data.meteredForElectricity)} +
+
+
+ ); +}; + +RemoteSurvey.propTypes = { + tableHeader: PropTypes.objectOf, + title: PropTypes.string, + data: PropTypes.objectOf, + upperFirstLetter: PropTypes.func, +}; + +export default RemoteSurvey; diff --git a/src/components/Ashp/ReviewAnswers/index.js b/src/components/Ashp/ReviewAnswers/index.js new file mode 100644 index 0000000000000000000000000000000000000000..034b60b30bdf802c2c391c4741323ea4e49961da --- /dev/null +++ b/src/components/Ashp/ReviewAnswers/index.js @@ -0,0 +1,52 @@ +import React from 'react'; +import PropTypes from 'prop-types'; +import BuildingInfo from './BuildingInfo'; +import RemoteSurvey from './RemoteSurvey'; +import FinancialInitial from './FinancialInitial'; + + +const ReviewAnswers = (props) => { + const upperFirstLetter = (str) => { + return str.charAt(0).toUpperCase() + str.slice(1); + }; + + return ( +
+
+
+ +
+
+ +
+
+ +
+
+
+ ); +}; + +ReviewAnswers.propTypes = { + tableHeader: PropTypes.objectOf, + buildingInfo: PropTypes.objectOf, + remoteSurvey: PropTypes.objectOf, + financialInitials: PropTypes.objectOf, +}; + +export default ReviewAnswers; diff --git a/src/components/LinkBarDetail/index.js b/src/components/LinkBarDetail/index.js index 6a8224d5cd2b4d199ccaade7ec36ff6126ddbfce..8505530394c266c7ce1dc2ce1a9a707cefbc9c57 100644 --- a/src/components/LinkBarDetail/index.js +++ b/src/components/LinkBarDetail/index.js @@ -36,7 +36,7 @@ const generateLinks = (rootURL, links) => { key={val.name} href={val.url} className={`pill persist ${val.tags}`} - target="_blank" + // target="_blank" > {val.name} diff --git a/src/components/SideBarDetail/index.js b/src/components/SideBarDetail/index.js index ee470758d7464e74b85c4a0253989926a26256fe..067653d96087f37096a883704f00dabc562db893 100644 --- a/src/components/SideBarDetail/index.js +++ b/src/components/SideBarDetail/index.js @@ -188,9 +188,9 @@ export default function SideBarDetail({ building, children, contacts, user }) { className={isActive(`${rootURL}/dimensions/`)} style={{ display: user.permissions['view::dimensions'] ? 'block' : 'none' }} > - + {dimensionSVG} - Dimensions + Questionnaire
  • diff --git a/src/containers/Questionnaire/actions.js b/src/containers/Questionnaire/actions.js new file mode 100644 index 0000000000000000000000000000000000000000..85d322bcc0c1ab144b43be2f012e1ac7a6bc0e24 --- /dev/null +++ b/src/containers/Questionnaire/actions.js @@ -0,0 +1,33 @@ +import { + LOAD_QUESTIONNAIRE_REQUESTED, + LOAD_QUESTIONNAIRE_SUCCEEDED, + LOAD_QUESTIONNAIRE_FAILED, + SUBMIT_QUESTIONNAIRE_REQUESTED, + SUBMIT_QUESTIONNAIRE_SUCCEEDED, + SUBMIT_QUESTIONNAIRE_FAILED, + CALCULATE_COST_REQUESTED, + CALCULATE_COST_SUCCEEDED, + CALCULATE_COST_FAILED, + DOWNLOAD_REPORT_REQUESTED, + DOWNLOAD_REPORT_SUCCEEDED, + DOWNLOAD_REPORT_FAILED, +} from './constants'; + +import { makeActionCreator } from '../../utils/reduxHelpers'; + + +export const loadQuestionnaire = makeActionCreator(LOAD_QUESTIONNAIRE_REQUESTED, 'buildingId'); +export const loadQuestionnaireSucceeded = makeActionCreator(LOAD_QUESTIONNAIRE_SUCCEEDED, 'questionnaire'); +export const loadQuestionnaireLFailed = makeActionCreator(LOAD_QUESTIONNAIRE_FAILED, 'error'); + +export const submitQuestionnaire = makeActionCreator(SUBMIT_QUESTIONNAIRE_REQUESTED, 'buildingId'); +export const submitQuestionnaireSucceeded = makeActionCreator(SUBMIT_QUESTIONNAIRE_SUCCEEDED, 'questionnaire'); +export const submitQuestionnaireFailed = makeActionCreator(SUBMIT_QUESTIONNAIRE_FAILED, 'error'); + +export const calculateCost = makeActionCreator(CALCULATE_COST_REQUESTED, 'buildingId', 'id'); +export const calculateCostSucceeded = makeActionCreator(CALCULATE_COST_SUCCEEDED, 'id'); +export const calculateCostFailed = makeActionCreator(CALCULATE_COST_FAILED, 'id', 'error'); + +export const downloadReport = makeActionCreator(DOWNLOAD_REPORT_REQUESTED, 'buildingId'); +export const downloadReportSucceeded = makeActionCreator(DOWNLOAD_REPORT_SUCCEEDED, 'payload'); +export const downloadReportFailed = makeActionCreator(DOWNLOAD_REPORT_FAILED, 'error'); diff --git a/src/containers/Questionnaire/constants.js b/src/containers/Questionnaire/constants.js new file mode 100644 index 0000000000000000000000000000000000000000..1f1286dee9172af4e197a7248f54d80724cc9ca6 --- /dev/null +++ b/src/containers/Questionnaire/constants.js @@ -0,0 +1,15 @@ +export const LOAD_QUESTIONNAIRE_REQUESTED = 'LOAD_QUESTIONNAIRE_REQUESTED'; +export const LOAD_QUESTIONNAIRE_SUCCEEDED = 'LOAD_QUESTIONNAIRE_SUCCEEDED'; +export const LOAD_QUESTIONNAIRE_FAILED = 'LOAD_QUESTIONNAIRE_FAILED'; + +export const SUBMIT_QUESTIONNAIRE_REQUESTED = 'SUBMIT_QUESTIONNAIRE_REQUESTED'; +export const SUBMIT_QUESTIONNAIRE_SUCCEEDED = 'SUBMIT_QUESTIONNAIRE_SUCCEEDED'; +export const SUBMIT_QUESTIONNAIRE_FAILED = 'SUBMIT_QUESTIONNAIRE_FAILED'; + +export const CALCULATE_COST_REQUESTED = 'CALCULATE_COST_REQUESTED'; +export const CALCULATE_COST_SUCCEEDED = 'CALCULATE_COST_SUCCEEDED'; +export const CALCULATE_COST_FAILED = 'CALCULATE_COST_FAILED'; + +export const DOWNLOAD_REPORT_REQUESTED = 'DOWNLOAD_REPORT_REQUESTED'; +export const DOWNLOAD_REPORT_SUCCEEDED = 'DOWNLOAD_REPORT_SUCCEEDED'; +export const DOWNLOAD_REPORT_FAILED = 'DOWNLOAD_REPORT_FAILED'; diff --git a/src/containers/Questionnaire/index.js b/src/containers/Questionnaire/index.js new file mode 100644 index 0000000000000000000000000000000000000000..e2e23f17f4c7f07f01244ded85a37df5ad9bbc47 --- /dev/null +++ b/src/containers/Questionnaire/index.js @@ -0,0 +1,406 @@ +import React, { Component } from 'react'; +import PropTypes from 'prop-types'; +import { connect } from 'react-redux'; +import { bindActionCreators } from 'redux'; +import { Button } from 'reactstrap'; +// import Loading from '../../components/Loading'; +import buildingDetailPropTypes from '../Building/propTypes'; +import { + loadQuestionnaire, + submitQuestionnaire, + calculateCost, + downloadReport, +} from './actions'; +import LinkBarDetail from '../../components/LinkBarDetail'; +import BuildingInfo from '../../components/Ashp/BuildingInfo'; +import FinancialInitial from '../../components/Ashp/FinancialInitial'; +import RemoteSurvey from '../../components/Ashp/RemoteSurvey'; +import ReviewAnswers from '../../components/Ashp/ReviewAnswers'; + + +class Questionnaire extends Component { + constructor(props) { + super(props); + this.nameMapping = { + firstName: 'First name', + lastName: 'Last name', + email: 'Email', + phone: 'Phone number', + heatingSystem: 'Heating System', + heatingFuelSource: 'Heating Fuel Source', + DHWSameBoiler: 'Domestic Hot Water', + AgeOfHeatGenerateSystem: 'Age of heat generating system', + planToReplaceHS: 'Plan to replace the heating system', + hallwaysHeated: 'Heated hallways', + basementHeated: 'Heated basement', + stairwellsHeated: 'Heated stairwells', + floorsHeated: 'Heated floors', + accessibleByStairwell: 'Stairwell accessibility', + backFacadeAttached: 'Back facade', + exteriorWallsAttached: 'Attached exterior walls', + tenantComplaintsComfort: 'Tenant temperature complaints', + tenantComplaintsWindows: 'Tenant window draft complaints', + meteredForElectricity: 'ConEd Electricity Meter', + bankruptyPast: 'Past bankruptcy', + currentOnEnergyBills: 'Energy bills status', + currentMortgageBalance: 'Outstanding mortgage balance', + profitable: 'Building profit', + budgetForDownPayment: 'Down payment budget', + }; + this.state = { + buildingInfo: { + firstName: 'Bryan', + lastName: 'Ontiveros', + email: 'bryan@blocpower.io', + phone: '911-911-9911', + address: this.props.building.street_address.map(addressItem => { + return `${addressItem}, ${this.props.building.borough}`; + }), + numOfStudio: 1, + numOfOneBedroom: 3, + numOfTwoBedroom: 2, + numOfThreeBedroom: 1, + numOfFourBedroom: 0, + other: ' ', + numOfOther: 0, + }, + remoteSurvey: { + heatingSystem: 'steam', + heatingFuelSource: 'heating oil', + DHWSameBoiler: 'yes', + AgeOfHeatGenerateSystem: '0-3 years', + planToReplaceHS: 'asap', + hallwaysHeated: 'yes', + basementHeated: 'yes', + stairwellsHeated: 'yes', + floorsHeated: 'yes', + accessibleByStairwell: 'yes', + backFacadeAttached: 'yes', + exteriorWallsAttached: 'no', + tenantComplaintsComfort: 'no', + tenantComplaintsWindows: 'no', + meteredForElectricity: 'no', + }, + financialInitials: { + bankruptyPast: 'yes', + currentOnEnergyBills: 'current', + currentMortgageBalance: '0%', + profitable: 'always', + budgetForDownPayment: 'no cash', + }, + showReviewAnswers: false, + submitted: false, + calculated: false, + }; + } + + componentDidMount() { + const buildingId = this.props.building.building_id; + this.props.loadQuestionnaire(buildingId); + } + + onChangeEvent = (formType, formData) => { + this.setState({ + ...this.state, + [formType]: formData, + }, () => { + console.log(this.state[formType]); // eslint-disable-line + }); + } + + submitQuestionnaire = () => { + if (this.validateInputs()) { + this.setState({ + submitted: true, + showReviewAnswers: true, + }, () => { + console.log('Saving answers!'); // eslint-disable-line + }); + } + return false; + } + + calculate = () => { + this.setState({ + submitted: false, + calculated: true, + }, () => { + console.log('Calculated!'); // eslint-disable-line + }); + } + + downloadReport = () => { + this.setState({ + calculated: false, + }, () => { + console.log('Dowmloaded!'); // eslint-disable-line + }); + } + + editForm = () => { + this.setState({ + showReviewAnswers: false, + submitted: false, + calculated: false, + }); + } + + validateInputs() { + const emptyFields = []; + const invalidFields = []; + const errorMessage = []; + const validEmail = /^$|^[a-zA-Z0-9]+@[a-zA-Z0-9]+\.[A-Za-z]+$/; + const validPhone = /^$|^\(?(\d{3})\)?[- ]?(\d{3})[- ]?(\d{4})$/; + + Object.entries(this.state).forEach(formData => { + if (['buildingInfo', 'remoteSurvey', 'financialInitials'].includes(formData[0])) { + Object.entries(formData[1]).forEach(fieldData => { + if (fieldData[0] !== 'other' && fieldData[1].toString().replace(/^\s+|\s+$/g, '') === '') { + emptyFields.push(this.nameMapping[fieldData[0]]); + } + if (fieldData[0] === 'phone' && !(validPhone.test(fieldData[1]))) { + invalidFields.push(this.nameMapping[fieldData[0]]); + } + if (fieldData[0] === 'email' && !(validEmail.test(fieldData[1]))) { + invalidFields.push(this.nameMapping[fieldData[0]]); + } + if (fieldData[1] === '-1') { + emptyFields.push(this.nameMapping[fieldData[0]]); + } + }); + } + }); + if (emptyFields.length > 0 || invalidFields.length > 0) { + if (emptyFields.length > 0) { + errorMessage.push(`Please fill in ${emptyFields.join(', ')} field(s)`); + } + if (invalidFields.length > 0) { + errorMessage.push(`\nInvalid input in ${invalidFields.join(', ')} field(s)`); + } + alert(errorMessage); + return false; + } + return true; + } + + render() { + const tableHeader = { + paddingBottom: '20px', + borderTop: '1px solid #CCCCCC', + background: '#EEEEEE', + fontWeight: 'bold', + paddingTop: '20px', + }; + + const labelStyle = { + marginBottom: '0.7em', + fontWeight: 'bold', + fontSize: '12px', + color: '#999999', + }; + + const successMsgStyle = { + padding: '15px 50px 15px 50px', + border: '2px solid #CCCCCC', + marginBottom: '20px', + background: '#B8DFC2', + }; + + const editStyle = { + marginLeft: '15px', + cursor: 'pointer', + textDecoration: 'underline', + }; + + let forms = ''; + if (this.state.showReviewAnswers === false) { + forms = ( +
    +
    +
    + +
    +
    +
    +
    + +
    +
    +
    +
    + +
    +
    +
    + ); + } + + let message = ''; + let editLink = ''; + let submitButton = ''; + if (this.state.showReviewAnswers === false) { + submitButton = ( + + ); + } + + let calculateButton = ''; + if (this.state.submitted === true) { + calculateButton = ( + + {' '} + + + ); + message = ( +
    + Successfully submitted. +
    + ); + } + + let downloadButton = ''; + if (this.state.calculated === true) { + downloadButton = ( + + {' '} + + + ); + message = ( +
    + Calculation completed. +
    + ); + } + + let reviewAnswers = null; + if (this.state.showReviewAnswers === true) { + reviewAnswers = ( + + ); + editLink = ( + + Edit answers + + ); + } + + const mainContent = ( +
    + {forms} +
    +
    + {message} + {submitButton} + {calculateButton} + {downloadButton} + {editLink} +
    +
    + {reviewAnswers} +
    + ); + + return ( +
    + + {mainContent} +
    + ); + } +} + +Questionnaire.propTypes = { + building: buildingDetailPropTypes, + loadQuestionnaire: PropTypes.func, +}; + +const mapDispatchToProps = dispatch => ( + bindActionCreators({ + loadQuestionnaire, + submitQuestionnaire, + calculateCost, + downloadReport, + }, dispatch) +); + +const mapStateToProps = state => ({ + buildingInfo: state.buildingInfo, + user: state.user, + eng: state.eng, +}); + +export default connect(mapStateToProps, mapDispatchToProps)(Questionnaire); diff --git a/src/containers/Questionnaire/reducer.js b/src/containers/Questionnaire/reducer.js new file mode 100644 index 0000000000000000000000000000000000000000..b932745ae439291fc684bc82e232206dfe929c8a --- /dev/null +++ b/src/containers/Questionnaire/reducer.js @@ -0,0 +1,156 @@ +import { + LOAD_QUESTIONNAIRE_REQUESTED, + LOAD_QUESTIONNAIRE_SUCCEEDED, + LOAD_QUESTIONNAIRE_FAILED, + SUBMIT_QUESTIONNAIRE_REQUESTED, + SUBMIT_QUESTIONNAIRE_SUCCEEDED, + SUBMIT_QUESTIONNAIRE_FAILED, + CALCULATE_COST_REQUESTED, + CALCULATE_COST_SUCCEEDED, + CALCULATE_COST_FAILED, + DOWNLOAD_REPORT_REQUESTED, + DOWNLOAD_REPORT_SUCCEEDED, + DOWNLOAD_REPORT_FAILED, +} from './constants'; + + +const questionnaireInitialState = { + buildingInfo: { + loading: false, + error: false, + data: null, + }, + remoteSurvey: { + loading: false, + error: false, + data: null, + }, + financialInitials: { + loading: false, + error: false, + data: null, + }, +}; + +export default function (state = questionnaireInitialState, action) { + switch (action.type) { + case LOAD_QUESTIONNAIRE_REQUESTED: + return { + ...state, + buildingInfo: { + ...state.buildingInfo, + loading: true, + error: false, + }, + }; + + case LOAD_QUESTIONNAIRE_SUCCEEDED: + return { + ...state, + buildingInfo: { + data: action.instance, + loading: false, + error: false, + }, + }; + + case LOAD_QUESTIONNAIRE_FAILED: + return { + ...state, + buildingInfo: { + loading: false, + error: action.error, + }, + }; + + case SUBMIT_QUESTIONNAIRE_REQUESTED: + return { + ...state, + remoteSurvey: { + ...state.remoteSurvey, + loading: true, + error: false, + }, + }; + + case SUBMIT_QUESTIONNAIRE_SUCCEEDED: + return { + ...state, + remoteSurvey: { + data: action.instance, + loading: false, + error: false, + }, + }; + + case SUBMIT_QUESTIONNAIRE_FAILED: + return { + ...state, + remoteSurvey: { + loading: false, + error: action.error, + }, + }; + + case CALCULATE_COST_REQUESTED: + return { + ...state, + financialInitials: { + ...state.financialInitials, + loading: true, + error: false, + }, + }; + + case CALCULATE_COST_SUCCEEDED: + return { + ...state, + financialInitials: { + data: action.instance, + loading: false, + error: false, + }, + }; + + case CALCULATE_COST_FAILED: + return { + ...state, + financialInitials: { + loading: false, + error: action.error, + }, + }; + + case DOWNLOAD_REPORT_REQUESTED: + return { + ...state, + financialInitials: { + ...state.financialInitials, + loading: true, + error: false, + }, + }; + + case DOWNLOAD_REPORT_SUCCEEDED: + return { + ...state, + financialInitials: { + data: action.instance, + loading: false, + error: false, + }, + }; + + case DOWNLOAD_REPORT_FAILED: + return { + ...state, + financialInitials: { + loading: false, + error: action.error, + }, + }; + + default: + return state; + } +} diff --git a/src/containers/Questionnaire/sagas.js b/src/containers/Questionnaire/sagas.js new file mode 100644 index 0000000000000000000000000000000000000000..da8db336925f9e47e77d295710b1ad11256b56cc --- /dev/null +++ b/src/containers/Questionnaire/sagas.js @@ -0,0 +1,70 @@ +import { takeEvery, put, call } from 'redux-saga/effects'; +import { getHeaders } from '../../utils/restServices'; +import request from '../../utils/request'; + +import { + LOAD_QUESTIONNAIRE_REQUESTED, + SUBMIT_QUESTIONNAIRE_REQUESTED, + CALCULATE_COST_REQUESTED, + DOWNLOAD_REPORT_REQUESTED, +} from './constants'; + +import { + loadQuestionnaireSucceeded, + loadQuestionnaireLFailed, + submitQuestionnaireSucceeded, + submitQuestionnaireFailed, + calculateCostSucceeded, + calculateCostFailed, + downloadReportSucceeded, + downloadReportFailed, +} from './actions'; + +import SagaRequests from '../../utils/sagaRequests'; + + +function* loadQuestionnaire(action) { + const url = `${process.env.REACT_APP_BLOCLINK_URL}/buildings/${action.buildingId}/questionnaire/`; + yield SagaRequests.get(action, url, loadQuestionnaireSucceeded, loadQuestionnaireLFailed); +} + +function* submitQuestionnaire(action) { + const formData = new FormData(); + Object.keys(action.payload).map(key => formData.append(key, action.payload[key])); + const url = `${process.env.REACT_APP_BLOCLINK_URL}/buildings/${action.buildingId}/questionnaire/`; + + const res = yield call( + request, + SagaRequests.generateURL(url, action), + { + method: 'POST', + headers: getHeaders(), + body: formData, + } + ); + + if (!res.err) { + yield put(submitQuestionnaireSucceeded(res)); + } else { + yield put(submitQuestionnaireFailed(res.err)); + } +} + +function* calculateCost(action) { + const url = `${process.env.REACT_APP_BLOCLINK_URL}/buildings/${action.buildingId}/calculate-cost/`; + yield SagaRequests.get(action, url, calculateCostSucceeded, calculateCostFailed); +} + +function* downloadReport(action) { + const url = `${process.env.REACT_APP_BLOCLINK_URL}/buildings/${action.buildingId}/download-report/`; + yield SagaRequests.get(action, url, downloadReportSucceeded, downloadReportFailed); +} + +function* engWatcher() { + yield takeEvery(LOAD_QUESTIONNAIRE_REQUESTED, loadQuestionnaire); + yield takeEvery(SUBMIT_QUESTIONNAIRE_REQUESTED, submitQuestionnaire); + yield takeEvery(CALCULATE_COST_REQUESTED, calculateCost); + yield takeEvery(DOWNLOAD_REPORT_REQUESTED, downloadReport); +} + +export default engWatcher; diff --git a/src/reducers.js b/src/reducers.js index 0044eac8fc7a27868856ca6aa899b4ef72a56f09..1cf26ccab1c433a6480c75b43073611c31ca1cf9 100644 --- a/src/reducers.js +++ b/src/reducers.js @@ -18,6 +18,7 @@ import weather from './containers/Weather/reducer'; import events from './containers/Event/reducer'; import blocnote from './containers/Blocnote/reducer'; import bloclink from './containers/Performance/reducer'; +import questionnaire from './containers/Questionnaire/reducer'; export default combineReducers({ @@ -39,4 +40,5 @@ export default combineReducers({ events, blocnote, bloclink, + questionnaire, }); diff --git a/src/routes.js b/src/routes.js index 63ea9d48c64f5a572b8556e728255030c9ebb511..68d48e2a9c4c6606672682df64018c98467c84a5 100644 --- a/src/routes.js +++ b/src/routes.js @@ -15,7 +15,7 @@ import AuditPage from './screens/AuditPage'; import DetailPage from './screens/DetailPage'; import ProjectDetailPage from './screens/ProjectDetailPage'; import BuildingOverview from './components/BuildingOverview'; -import Dimensions from './containers/Dimensions'; +// import Dimensions from './containers/Dimensions'; import ReportsHome from './screens/ReportsHome'; import Utilities from './components/Utilities'; import Envelope from './containers/Envelope'; @@ -32,6 +32,7 @@ import Wrapper from './containers/Wrapper'; import { BGroupOverview, BGroup } from './containers/BGroup'; import NavOnly from './screens/NavOnly/NavOnly'; import Admin from './containers/Admin/Admin'; +import Questionnaire from './containers/Questionnaire'; const auth = new Auth(); @@ -52,7 +53,8 @@ export default ( - + {/* */} + diff --git a/src/sagas.js b/src/sagas.js index 7d7ea2671868677c2ddc156ed89857d2754d92c2..364d621ad15a4d561579a54531409413a3d1e3ad 100644 --- a/src/sagas.js +++ b/src/sagas.js @@ -15,6 +15,7 @@ import weatherSaga from './containers/Weather/sagas'; import eventsSaga from './containers/Event/sagas'; import blocnoteSaga from './containers/Blocnote/sagas'; import performanceSaga from './containers/Performance/sagas'; +import questionnaireSaga from './containers/Questionnaire/sagas'; export default function* rootSaga() { @@ -36,5 +37,6 @@ export default function* rootSaga() { eventsSaga(), blocnoteSaga(), performanceSaga(), + questionnaireSaga(), ]; }