diff --git a/src/AnotherBuilding.js b/src/AnotherBuilding.js new file mode 100644 index 0000000000000000000000000000000000000000..5e6e1d86cb31f8e54d141909d2c3987cd0948dfa --- /dev/null +++ b/src/AnotherBuilding.js @@ -0,0 +1,336 @@ +import React from "react"; +import { Row, Col, Button, Input, Modal, ModalHeader, ModalBody, ModalFooter } from 'reactstrap'; +import './index.css'; +import Geocoder from 'react-mapbox-gl-geocoder'; +import location from './utils/images/location.svg'; // with import +import jsonp from "jsonp"; +import ReactGA from 'react-ga'; + +const mapAccess = { + mapboxApiAccessToken: process.env.REACT_APP_MAPBOX_TOKEN +} + +const queryParams = { + country: 'us' +} + +export default class AnotherBuilding extends React.Component { + constructor(props) { + super(props); + this.state = { + viewport: {}, + addressFound: false, + answer: this.props.answer, + loadingAddressSearchBtn: false, + disabledAddressSearchBtn: true, + showModal: true, + form: { + FNAME: '', + LNAME: '', + EMAIL: '', + PHONE: '', + }, + subscribed: false, + }; + } + componentDidMount () { + let url = document.URL + let key = url.split('?')[1] + if(key !== undefined){ + let address = decodeURI(key).split('address=')[1] + if (address !== "undefined"){ + if (address.length !== 0){ + this.setState({ + answer: address, + addressFound: true, + }); + } + } + } + if(process.env.REACT_APP_ENVIRONMENT==='production'){ + ReactGA.pageview('/address-search'); + } + } + + handleOnSelected = (viewport, item) => { + this.setState({viewport}); + this.setState({ + answer: item.place_name, + addressFound: true, + }, () => { + this.validateEmptyInputs(); + }); + } + + validateEmptyInputs = () => { + this.state.answer.length > 0 ? + this.setState({ disabled: false }) : this.setState({ disabled: true }); + } + + existAddressSubmitted = () => { + this.props.nextQuestion('anotherBuilding', this.props.answer, this.props.answer); + } + + newAddressSubmitted = () => { + if(process.env.REACT_APP_ENVIRONMENT==='production'){ + ReactGA.event({ + category: "Intake Survey NYC", + action: "Clicked continue button", + label: "address-search" + }); + } + this.setState({ + loadingAddressSearchBtn: true, + disabledAddressSearchBtn: true, + showModal: true, + }); + this.props.nextQuestion('anotherBuilding', this.state.answer, this.state.answer); + } + + disableText = () => { + this.setState({ + addressFound: false , + loadingAddressSearchBtn: false, + disabledAddressSearchBtn: true, + }); + } + + validateContact() { + const emptyFields = []; + const invalidFields = []; + const errorMessage = []; + const validFirstName = /^[a-zA-Z ]{2,30}$/; + const validLastName = /^[a-zA-Z ]{2,30}$/; + const validEmail = /^\w+([\.-]?\w+)*@\w+([\.-]?\w+)*(\.\w+)+$/; + const validPhone = /^$|^\(?(\d{3})\)?[- ]?(\d{3})[- ]?(\d{4})$/; + + if (this.state.form.FNAME.toString().replace(/^\s+|\s+$/g, '') === '') { + emptyFields.push('First Name'); + } + if (this.state.form.LNAME.toString().replace(/^\s+|\s+$/g, '') === '') { + emptyFields.push('Last Name'); + } + if (this.state.form.EMAIL.toString().replace(/^\s+|\s+$/g, '') === '') { + emptyFields.push('Email address'); + } + if (emptyFields.length > 0) { + errorMessage.push(`Please fill in:\n${emptyFields.join('\n')}`); + alert(errorMessage); + return false; + } + if (this.state.form.PHONE.toString().replace(/^\s+|\s+$/g, '') !== '' && + !(validPhone.test(this.state.form.PHONE))) { + invalidFields.push('Phone number'); + } + if (!(validFirstName.test(this.state.form.FNAME))) { + invalidFields.push('First Name'); + } + if (!(validLastName.test(this.state.form.LNAME))) { + invalidFields.push('Last Name'); + } + if (!(validEmail.test(this.state.form.EMAIL))) { + invalidFields.push('Email address'); + } + if (invalidFields.length > 0) { + errorMessage.push(`\nInvalid input in:\n${invalidFields.join('\n')}`); + alert(errorMessage); + return false; + } + return true; + } + + handleInputChange = (event) => { + const val = event.target.value; + const name = event.target.name; + this.setState({ + form: { + ...this.state.form, + [name]: val, + }, + }); + } + + subscribe = () => { + if (this.validateContact() && process.env.NODE_ENV === 'production') { + const action = process.env.REACT_APP_MAILCHIMP_URL; + const fields = ['FNAME', 'LNAME', 'EMAIL', 'PHONE']; + const values = fields.map(field => { + return `${field}=${encodeURIComponent(this.state.form[field])}`; + }).join("&"); + const path = `${action}&${values}`; + const url = path.replace('/post?', '/post-json?'); + jsonp(url, { param: "c" }); + this.setState({ + subscribed: true, + }); + } + // If environment is not production close the form. + // Commenting out the below code because it closes the form on invalid inputs. + // else { + // this.setState({ + // showModal: false, + // }) + // } + } + + closeModal = () => { + this.setState({ + showModal: false, + }) + } + + render() { + let addressData = ""; + const {viewport} = this.state; + if(this.state.addressFound === false){ + addressData = ( + + ) + } + else { + addressData = ( + + ) + } + + let modalHeader = 'Oops, something is wrong'; + let modalBody = this.props.message; + let modalFooter = ( +
+ +
+ ); + + if (this.props.message === 'Sorry! Area not covered at the moment.') { + modalHeader = this.state.subscribed ? 'Congratulations!' : 'Sorry, we\'re not in your city yet!'; + modalBody = this.state.subscribed ? 'You have been successfully subscribed.' : ( +
+
+ We apologize, your area isn't covered by BlocPower yet, but we will get in touch once we introduce service there. +
+ + + this.handleInputChange(e)} + /> + + + this.handleInputChange(e)} + /> + + + + + this.handleInputChange(e)} + /> + + + + + this.handleInputChange(e)} + /> + + +
+ ); + modalFooter = this.state.subscribed ? ( +
+ +
+ ) : ( +
+ {' '} + +
+ ); + } + + let message = ( + + + {modalHeader} + + + {modalBody} + + + {modalFooter} + + + ); + + const content = ( +
+ {message} +
+ The next building I want to tell you about is at the same address (click the button below) + +
+
+
+ The next building I want to tell you about is at a new address (enter address below) +
+ + + + + + {addressData} + + +
+ + + Having trouble entering your address?
+ Please email us at support@blocpower.io + + + + +
+
+ ); + + return ( +
+ {content} +
+ ); + } +} diff --git a/src/App.css b/src/App.css index 42eef8fb04b581266e6e2890ba1b4fd3cdcbdcc1..ea210ab9fc668848c8a5ddba6b43a44211ec1c6d 100644 --- a/src/App.css +++ b/src/App.css @@ -244,6 +244,18 @@ width: 60%; } +.anotherBuildingExistBuilding { + margin-bottom: 8%; +} + +.anotherBuildingExistBuildingButton { + margin-top: 2%; +} + +.anotherBuildingNewBuildingNote { + margin-bottom: 15px; +} + @media (max-width: 1367px) { .main-content { padding: 300px 20% 4% 10%; diff --git a/src/App.js b/src/App.js index e88f1ed07b900d568e58e4c3612b2c5d44b71697..49375cfc5d602b4d82ec1f9a51640ecdf779c7f1 100644 --- a/src/App.js +++ b/src/App.js @@ -33,6 +33,7 @@ import ChurchCoolingSystemAge from './ChurchCoolingSystemAge'; import ChurchBuildingType from './ChurchBuildingType'; import ChurchHoursPerWeek from './ChurchHoursPerWeek'; import OperationalSeasons from './OperationalSeasons'; +import AnotherBuilding from './AnotherBuilding'; import { questionTree } from './constants'; import { connect } from 'react-redux'; import { submitContact, submitAnswer, submitBuilding, submitQuestionnaire, submitUserAnswer } from './actions'; @@ -64,16 +65,34 @@ class App extends React.Component { waitList={false} oakland={false} milwaukee={false} + anotherBuilding={this.anotherBuilding} />, congrats: , }, multiFamilyAnswerId: 21, miniSplitCheckBox: false, + anotherBuildingSubmitted: false, }; } + componentWillReceiveProps(nextProps) { + // If user submitted another building search, use the new building object data + if (nextProps.building !== undefined && this.props.building !== undefined && + nextProps.building.data.userId !== this.props.building.data.userId && + this.state.anotherBuildingSubmitted === true) { + this.props.onSubmitAnswer({ + questionId: ["6"], + answerId: [32], + surveyId: nextProps.building.data.surveyId, + buildingId: nextProps.building.data.buildingId, + userId: nextProps.building.data.userId, + }); + } + } + resetQuestions = () => { return { addressSearch: { @@ -290,7 +309,8 @@ class App extends React.Component { 81: 'HelpFindingContractors', 82: 'AdviceOnEnergyWaterEfficiency', 83: 'HelpFinancingEnergyProject', - 33: 'other' }, + 33: 'other', + }, prev: 'buildingTypeSubType', next: 'buildingOperatingChallenges', }, @@ -705,6 +725,13 @@ class App extends React.Component { prev: 'buildingPastImprovement', next: '', }, + anotherBuilding: { + title: '', + answer: '', + value: '', + prev: 'anotherBuilding', + next: 'churchBuildingType', + }, order: [], }; } @@ -733,15 +760,23 @@ class App extends React.Component { questionAnswers[questions[question].id] = value; } + if (question === 'anotherBuilding') { + questions['churchBuildingType']['prev'] = 'anotherBuilding'; + } + const updatedOrder = questions.order.filter(item => item !== question); updatedOrder.push(question); questions.order = updatedOrder; this.setState({ questions }, () => { - if (question === 'addressSearch') { + if (['addressSearch', 'anotherBuilding'].includes(question)) { this.props.onSubmitBuilding({ address: this.state.questions[question].value, }); + // If user clicks on 'continue' button on another building search page + if (question === 'anotherBuilding') { + this.setState({ anotherBuildingSubmitted: true }); + } } else if (question === 'unitBreakdown' || question === 'feedback'){ this.setQuestion(this.state.questions[question].next); this.props.onSubmitUserAnswer({ @@ -751,6 +786,7 @@ class App extends React.Component { buildingId: this.props.building.data.buildingId, userId: this.props.building.data.userId, }); + this.setState({ anotherBuildingSubmitted: false }); } else { this.setQuestion(this.state.questions[question].next); this.props.onSubmitAnswer({ @@ -760,6 +796,7 @@ class App extends React.Component { buildingId: this.props.building.data.buildingId, userId: this.props.building.data.userId, }); + this.setState({ anotherBuildingSubmitted: false }); } }); @@ -844,15 +881,33 @@ class App extends React.Component { } // If building type is not single fam and fuel type is natural gas or dual fuel -> display congrats page. - if (questions['unitBreakdown']['value'] !== 20 && - question === 'fuels' && [4,6].includes(value)){ + if (questions['unitBreakdown']['value'] !== 20 && question === 'fuels' && [4,6].includes(value)){ this.setState({ buildingQualified: true}); this.state.pages.congrats = ; + } else { + this.state.pages.congrats = ; } } + anotherBuilding = () => { + // When user starts new loop of questionnaire, we reset all the answers + const questions = this.resetQuestions(); + questions['addressSearch'].value = this.state.questions['addressSearch'].value; + questions['buildingTypes'].value = this.state.questions['buildingTypes'].value; + this.setState({ + questions, + question: 'anotherBuilding', + }); + } + submitContact = (contact) => { this.setQuestion(this.state.questions['contact'].next); this.setState({ @@ -878,6 +933,7 @@ class App extends React.Component { prevQuestion = (question) => { this.setQuestion(this.state.questions[question].prev); + this.setState({ anotherBuildingSubmitted: false }); } setQuestion = (question) => { @@ -912,34 +968,22 @@ class App extends React.Component { } submit = () => { - // 20 is the id for single family building type - if (this.state.questions.buildingTypes.value === 20) { - this.state.pages.thankYou = ; - } + // Assign property values for last pages (thankyou and congrats) + const isSingleFamily = this.state.questions.buildingTypes.value === 20 || this.state.questions.waitList.value === 1 ? true : false; + const isWorshipBuilding = this.state.questions.buildingTypes.value === 32 ? true : false; + const anotherBuilding = this.state.questions.buildingTypes.value === 32 ? this.anotherBuilding : null; + const isWaitList = this.state.questions.waitList.value === 1 ? true : false; + const isOakland = this.props.building.data.surveyId === 2 ? true : false; + const isMilwaukee = this.props.building.data.surveyId === 3 ? true : false; - // 1 - Yes & 2 - No for joining the waitlist - if(this.state.questions.waitList.value === 1) { - this.state.pages.thankYou = ; - } - - // Oakland Survey ID = 2 - if(this.props.building.data.surveyId === 2) { - this.state.pages.thankYou = ; - } - - // Milwaukee Survey ID = 3 - if(this.props.building.data.surveyId === 3) { - this.state.pages.thankYou = ; - } + this.state.pages.thankYou = ; this.setState({ submitted: true, @@ -1187,6 +1231,13 @@ class App extends React.Component { nextQuestion={this.nextQuestion} questions={this.state.questions['buildingFutureImprovement'].questions} />, + anotherBuilding: , }; let content = ""; @@ -1196,9 +1247,10 @@ class App extends React.Component { Object.keys(this.state.questions).indexOf(this.state.question) > -1) { if (this.state.question === 'addressSearch' && this.props.building.data.surveyId === 1) { content = questions['buildingTypes']; - } - else if (this.state.question === 'addressSearch' && [2,3].includes(this.props.building.data.surveyId)){ + } else if (this.state.question === 'addressSearch' && [2,3].includes(this.props.building.data.surveyId)){ content = questions['buildingTypeSubType']; + } else if (this.state.anotherBuildingSubmitted) { + content = questions['churchBuildingType']; } else { content = questions[this.state.question]; } diff --git a/src/CompleteButtons.js b/src/CompleteButtons.js index 68dedc9e1b79f642c191f786657f53e0c08e48e1..d5524c744913d308778f69c390eb5f78c5cd8710 100644 --- a/src/CompleteButtons.js +++ b/src/CompleteButtons.js @@ -3,12 +3,19 @@ import { Row, Col, Button } from 'reactstrap'; import './index.css'; export default class CompleteButtons extends React.Component { - addBuilding = () => { - console.log('yes'); + anotherBuilding = () => { + // if(process.env.REACT_APP_ENVIRONMENT==='production'){ + // ReactGA.event({ + // category: "Intake Survey General", + // action: "Clicked continue button", + // label: "building-needs" + // }); + // } + this.props.anotherBuilding(); } closePage = () => { - window.close(); + window.open('location', '_self', '').close(); } render() { @@ -18,7 +25,7 @@ export default class CompleteButtons extends React.Component { diff --git a/src/Congrats.js b/src/Congrats.js index d90ad302b7ccb907b007661a6576de0df6e64fd1..40aad92cfd7677b686e12125f99320de08ec299a 100644 --- a/src/Congrats.js +++ b/src/Congrats.js @@ -1,6 +1,7 @@ import React from "react"; import congrats from './utils/images/spot-results-congratulations.svg'; // with import import './index.css'; +import CompleteButtons from './CompleteButtons'; import ReactGA from 'react-ga'; export default class Congrats extends React.Component { @@ -14,6 +15,10 @@ export default class Congrats extends React.Component { } } + anotherBuilding = () => { + this.props.anotherBuilding(); + } + render() { const items = [ 'Air source heat pumps use less energy than your current system', @@ -29,6 +34,17 @@ export default class Congrats extends React.Component { const data = this.props.naturalGas === true ? items.slice(1).map(item => { return (
  • {item}
  • ) }) : items.map(item => { return (
  • {item}
  • ) }); + + + let buttons = ''; + if (this.props.worshipBuilding === true) { + buttons = ( + + ); + } + const content = (
    @@ -48,6 +64,7 @@ export default class Congrats extends React.Component {
    We'll send you an email to find a time to chat or you can call us at {process.env.REACT_APP_BLOCPOWER_CONTACT}.
    + { buttons }
    ); diff --git a/src/ThankYou.js b/src/ThankYou.js index 7194812a6ab7b71ec5f73b5a18bd058578eac69a..f87d0ef0bb7b09d577d8691f1715b58e4bf74dfc 100644 --- a/src/ThankYou.js +++ b/src/ThankYou.js @@ -1,6 +1,7 @@ import React from "react"; import result from './utils/images/spot-results-more-info.svg'; // with import import './index.css'; +import CompleteButtons from './CompleteButtons'; import ReactGA from 'react-ga'; export default class ThankYou extends React.Component { @@ -14,29 +15,33 @@ export default class ThankYou extends React.Component { } } + anotherBuilding = () => { + this.props.anotherBuilding(); + } + render() { // If building type is not single family (for NYC BIS) and for other surveys - var data = + var data =
    Looks like we need more information to see if your building is a good fit for air source heat pumps. We'll send you an email to find a time to chat or you can {' '} - call us at {process.env.REACT_APP_BLOCPOWER_CONTACT}. + call us at {process.env.REACT_APP_BLOCPOWER_CONTACT}.
    // If building type is single family and user has joined the waitlist if (this.props.singleFamily === true) { - data = + data = this.props.waitList === true ?
    Thanks for joining our waitlist! We'll be in touch as soon as we expand our services!
    - If you’d like to reach out to us in the meantime you can call us at {process.env.REACT_APP_BLOCPOWER_CONTACT}. + If you’d like to reach out to us in the meantime you can call us at {process.env.REACT_APP_BLOCPOWER_CONTACT}.
    :
    Sorry to see you go! Come back any time if you change your mind!
    - } + } if (this.props.oakland === true ||this.props.milwaukee === true) { const email = this.props.milwaukee === true ? @@ -45,16 +50,25 @@ export default class ThankYou extends React.Component { const emailURL = this.props.milwaukee === true ? "mailto:welcome.milwaukee@blocpower.io": "mailto:welcome.oakland@blocpower.io" - data = + data =
    Looks like we need more information to see what energy saving projects are a good fit for your building.
    - We’ll send you an email to find a time to chat or you - can call us at {process.env.REACT_APP_BLOCPOWER_CONTACT} + We’ll send you an email to find a time to chat or you + can call us at {process.env.REACT_APP_BLOCPOWER_CONTACT} or email us at {email}. -
    - } - + + } + + let buttons = ''; + if (this.props.worshipBuilding === true) { + buttons = ( + + ); + } + const content = (
    @@ -62,6 +76,7 @@ export default class ThankYou extends React.Component { Thank You!
    { data } + { buttons } );