diff --git a/.env b/.env index 667ad2b324cb604bf2a4a6bacd0d3f69fdd14e87..8593f5822dd9dc7f3bda4ba40a2d1f08cfd122ae 100644 --- a/.env +++ b/.env @@ -1,6 +1,7 @@ -mapboxApiAccessToken= 'pk.eyJ1IjoiYmxvY3Bvd2VyIiwiYSI6ImNqd202bngzazE3c2o0OW4wM2IzbG00Y2cifQ.ZdSoYvbdw4fuIul8PQ3sBQ' +REACT_APP_MAPBOX_TOKEN=pk.eyJ1IjoiYmxvY3Bvd2VyIiwiYSI6ImNqNDdhZ2o2czAwdDcycXJ1YzZ6MHZlNHoifQ.fRQhMFfCCyDk78RI5QD4ow # Environment ENVIRONMENT=local # Services REACT_APP_BLOCLINK_URL=http://0.0.0.0:5410 +SKIP_PREFLIGHT_CHECK=true diff --git a/package-lock.json b/package-lock.json index 47bbb4f2de73057657aa8499d1396c09c3d1e0f4..b12f58fba653b715764f09b7bd332ab4888e7761 100644 --- a/package-lock.json +++ b/package-lock.json @@ -2811,7 +2811,8 @@ }, "ansi-regex": { "version": "2.1.1", - "bundled": true + "bundled": true, + "optional": true }, "aproba": { "version": "1.2.0", @@ -2829,11 +2830,13 @@ }, "balanced-match": { "version": "1.0.0", - "bundled": true + "bundled": true, + "optional": true }, "brace-expansion": { "version": "1.1.11", "bundled": true, + "optional": true, "requires": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" @@ -2846,15 +2849,18 @@ }, "code-point-at": { "version": "1.1.0", - "bundled": true + "bundled": true, + "optional": true }, "concat-map": { "version": "0.0.1", - "bundled": true + "bundled": true, + "optional": true }, "console-control-strings": { "version": "1.1.0", - "bundled": true + "bundled": true, + "optional": true }, "core-util-is": { "version": "1.0.2", @@ -2957,7 +2963,8 @@ }, "inherits": { "version": "2.0.3", - "bundled": true + "bundled": true, + "optional": true }, "ini": { "version": "1.3.5", @@ -2967,6 +2974,7 @@ "is-fullwidth-code-point": { "version": "1.0.0", "bundled": true, + "optional": true, "requires": { "number-is-nan": "^1.0.0" } @@ -2979,17 +2987,20 @@ "minimatch": { "version": "3.0.4", "bundled": true, + "optional": true, "requires": { "brace-expansion": "^1.1.7" } }, "minimist": { "version": "0.0.8", - "bundled": true + "bundled": true, + "optional": true }, "minipass": { "version": "2.3.5", "bundled": true, + "optional": true, "requires": { "safe-buffer": "^5.1.2", "yallist": "^3.0.0" @@ -3006,6 +3017,7 @@ "mkdirp": { "version": "0.5.1", "bundled": true, + "optional": true, "requires": { "minimist": "0.0.8" } @@ -3078,7 +3090,8 @@ }, "number-is-nan": { "version": "1.0.1", - "bundled": true + "bundled": true, + "optional": true }, "object-assign": { "version": "4.1.1", @@ -3088,6 +3101,7 @@ "once": { "version": "1.4.0", "bundled": true, + "optional": true, "requires": { "wrappy": "1" } @@ -3163,7 +3177,8 @@ }, "safe-buffer": { "version": "5.1.2", - "bundled": true + "bundled": true, + "optional": true }, "safer-buffer": { "version": "2.1.2", @@ -3193,6 +3208,7 @@ "string-width": { "version": "1.0.2", "bundled": true, + "optional": true, "requires": { "code-point-at": "^1.0.0", "is-fullwidth-code-point": "^1.0.0", @@ -3210,6 +3226,7 @@ "strip-ansi": { "version": "3.0.1", "bundled": true, + "optional": true, "requires": { "ansi-regex": "^2.0.0" } @@ -3248,11 +3265,13 @@ }, "wrappy": { "version": "1.0.2", - "bundled": true + "bundled": true, + "optional": true }, "yallist": { "version": "3.0.3", - "bundled": true + "bundled": true, + "optional": true } } }, @@ -6968,7 +6987,8 @@ }, "ansi-regex": { "version": "2.1.1", - "bundled": true + "bundled": true, + "optional": true }, "aproba": { "version": "1.2.0", @@ -6986,11 +7006,13 @@ }, "balanced-match": { "version": "1.0.0", - "bundled": true + "bundled": true, + "optional": true }, "brace-expansion": { "version": "1.1.11", "bundled": true, + "optional": true, "requires": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" @@ -7003,15 +7025,18 @@ }, "code-point-at": { "version": "1.1.0", - "bundled": true + "bundled": true, + "optional": true }, "concat-map": { "version": "0.0.1", - "bundled": true + "bundled": true, + "optional": true }, "console-control-strings": { "version": "1.1.0", - "bundled": true + "bundled": true, + "optional": true }, "core-util-is": { "version": "1.0.2", @@ -7114,7 +7139,8 @@ }, "inherits": { "version": "2.0.3", - "bundled": true + "bundled": true, + "optional": true }, "ini": { "version": "1.3.5", @@ -7124,6 +7150,7 @@ "is-fullwidth-code-point": { "version": "1.0.0", "bundled": true, + "optional": true, "requires": { "number-is-nan": "^1.0.0" } @@ -7136,17 +7163,20 @@ "minimatch": { "version": "3.0.4", "bundled": true, + "optional": true, "requires": { "brace-expansion": "^1.1.7" } }, "minimist": { "version": "0.0.8", - "bundled": true + "bundled": true, + "optional": true }, "minipass": { "version": "2.3.5", "bundled": true, + "optional": true, "requires": { "safe-buffer": "^5.1.2", "yallist": "^3.0.0" @@ -7163,6 +7193,7 @@ "mkdirp": { "version": "0.5.1", "bundled": true, + "optional": true, "requires": { "minimist": "0.0.8" } @@ -7235,7 +7266,8 @@ }, "number-is-nan": { "version": "1.0.1", - "bundled": true + "bundled": true, + "optional": true }, "object-assign": { "version": "4.1.1", @@ -7245,6 +7277,7 @@ "once": { "version": "1.4.0", "bundled": true, + "optional": true, "requires": { "wrappy": "1" } @@ -7320,7 +7353,8 @@ }, "safe-buffer": { "version": "5.1.2", - "bundled": true + "bundled": true, + "optional": true }, "safer-buffer": { "version": "2.1.2", @@ -7350,6 +7384,7 @@ "string-width": { "version": "1.0.2", "bundled": true, + "optional": true, "requires": { "code-point-at": "^1.0.0", "is-fullwidth-code-point": "^1.0.0", @@ -7367,6 +7402,7 @@ "strip-ansi": { "version": "3.0.1", "bundled": true, + "optional": true, "requires": { "ansi-regex": "^2.0.0" } @@ -7405,11 +7441,13 @@ }, "wrappy": { "version": "1.0.2", - "bundled": true + "bundled": true, + "optional": true }, "yallist": { "version": "3.0.3", - "bundled": true + "bundled": true, + "optional": true } } } diff --git a/src/AddressSearch.js b/src/AddressSearch.js index 1708555646591bd7ea481675f23a50ad1243ccbb..0019a3c69e8a6f7fd04ebfd7ba423906fb0df5f7 100644 --- a/src/AddressSearch.js +++ b/src/AddressSearch.js @@ -5,7 +5,7 @@ import Geocoder from 'react-mapbox-gl-geocoder'; import location from './utils/images/location.svg'; // with import const mapAccess = { - mapboxApiAccessToken: 'pk.eyJ1IjoiYmxvY3Bvd2VyIiwiYSI6ImNqd202bngzazE3c2o0OW4wM2IzbG00Y2cifQ.ZdSoYvbdw4fuIul8PQ3sBQ' + mapboxApiAccessToken: process.env.REACT_APP_MAPBOX_TOKEN } const queryParams = { @@ -48,42 +48,61 @@ export default class AddressSearch extends React.Component { } render() { - const {viewport} = this.state - + let url = document.URL + let address = decodeURI(url.split('.io/')[1]) let addressData = ""; - if(this.state.addressFound === false){ + if(address === "undefined"){ + const {viewport} = this.state; + if(this.state.addressFound === false){ + addressData = ( + + ) + } else { addressData = ( - + ) - } else { + } + } + else{ addressData = ( - + ) + } + + let message = ""; + if(this.props.message !== '') { + message = ( +
+ Sorry. {this.props.message}! +
+ ); } + const content = (
+ {message}
What is your building address?
We'll use this information to determine if BlocPower is available in your area.
- - + + {addressData} - + Having trouble entering your address?
Please email us at support@blocpower.io diff --git a/src/App.css b/src/App.css index c7fe86bcf814bda397c0ded16a243e49a8573a2e..53ddbf8070ceb6def4eb5e96e6770758b3f7124e 100644 --- a/src/App.css +++ b/src/App.css @@ -111,6 +111,20 @@ width: 100%; } +.addressSearch { + width: 70%; + margin-left: 15% !important; + margin-right: 15% !important; +} + +.addressSearchIcon { + background: #333333; + color: #FFFFFF; + padding-top: 3px; + margin-left: 15px; + height: 35px; +} + .addressSearchSubTitle { font-size: 1em; margin-top: 25px; @@ -131,6 +145,17 @@ margin-bottom: 20px; } +.errorMessage { + padding: 15px; + border: 2px solid #CCCCCC; + width: 50%; + margin-left: 25%; + margin-right: 25%; + margin-bottom: 5%; + color: crimson; + background: #EFEFEF; +} + .react-geocoder input { width: 100% !important; } @@ -216,7 +241,7 @@ width: 100%; color: #002F43; font-weight: bold; - padding-left: 0; + padding-left: 0; padding-right: 0; font-family: 'AvenirNext'; } diff --git a/src/App.js b/src/App.js index 85a2b2fd785810fe7b763d4ca052de28b7cbb430..38bae9f269abde70c025f569667535ce4431439f 100644 --- a/src/App.js +++ b/src/App.js @@ -14,36 +14,30 @@ import ThankYou from './ThankYou'; import Congrats from './Congrats'; import FinanceInterest from './FinanceInterest'; import WaitList from './WaitList'; +import Contact from './Contact'; import ReviewAnswers from './ReviewAnswers'; -import phone from './utils/images/icon-phone.svg'; -import { questionTree, initialQuestions } from './constants'; +import { questionTree } from './constants'; import { connect } from 'react-redux'; -import { verifyUser, verifyToken, submitAnswer, submitBuilding, completeQuestionnaire } from './actions'; +import { submitContact, submitAnswer, submitBuilding, submitQuestionnaire } from './actions'; import './App.css'; import jsonp from "jsonp"; import ReactGA from 'react-ga'; + class App extends React.Component { constructor(props) { super(props); this.state = { - isReturningUser: false, - isNewUser: true, submitted: false, disabled: true, - selectedBuildingId: -1, - showUserAlert: false, - showTokenAlert: true, - token: '', - userBuildingId: -1, + surveyId: -1, + buildingId: -1, buildingQualified: false, form: { - FNAME: '', - LNAME: '', - EMAIL: '', - PHONE: '', - returningEmail: '', - address: '', + firstName: '', + lastName: '', + email: '', + phoneNumber: '', }, factors: { fuel: '', @@ -52,7 +46,8 @@ class App extends React.Component { heatingSystemPlan: '', buildingTypes: '', }, - question: '', + prevQuestion: '', + question: 'addressSearch', reviewQuestions: [], questions: this.resetQuestions(), pages: { @@ -70,6 +65,7 @@ class App extends React.Component { title: 'What is your building address?', answer: '', value: '', + prev: 'addressSearch', next: 'fuels', }, fuels: { @@ -158,7 +154,7 @@ class App extends React.Component { value: 0, answerIds: [1, 2], prev: '', - next: 'reviewAnswers', + next: 'contact', }, waitList: { id: 10, @@ -167,6 +163,11 @@ class App extends React.Component { value: 0, answerIds: [1, 2], prev: 'buildingTypes', + next: 'contact', + }, + contact: { + title: 'Your Information', + prev: '', next: 'reviewAnswers', }, reviewAnswers: { @@ -176,17 +177,7 @@ class App extends React.Component { }; } - componentDidMount() { - const url = window.location.href; - const token = url.substring(url.lastIndexOf('/') + 1); - if(token !== ''){ - this.verifyToken(token); - } - } - - prevQuestion = (question) => { - this.setQuestion(this.state.questions[question].prev); - } + componentDidMount() { } nextQuestion = (question, answer, value) => { const questionAnswers = {}; @@ -204,30 +195,54 @@ class App extends React.Component { questionAnswers[questions[question].id] = value; } + questions.financeInterest.next = "contact"; this.setState({ questions }, () => { this.setQuestion(this.state.questions[question].next); if (question === 'addressSearch') { this.props.onSubmitBuilding({ address: this.state.questions[question].value, - token: this.state.token, }); } else { - const userBuildingId = Object.keys(this.props.buildings.data)[0] === undefined - ? this.state.userBuildingId : Object.keys(this.props.buildings.data)[0]; Object.keys(questionAnswers).forEach(questionId => { this.props.onSubmitAnswer({ questionId: questionId, answerId: questionAnswers[questionId], - userBuildingId, - // userBuildingId: Object.keys(this.props.buildings.data)[0], + surveyId: this.props.building.data.surveyId, + buildingId: this.props.building.data.buildingId, + userId: this.props.building.data.userId, }); }); } }); } + submitContact = (contact) => { + this.setQuestion(this.state.questions['contact'].next); + this.setState({ + form: { + firstName: contact.firstName, + lastName: contact.lastName, + phoneNumber: contact.phoneNumber, + email: contact.email, + buildingId: this.props.building.data.buildingId, + userId: this.props.building.data.userId, + }, + }, () => { + this.props.onSubmitContact(this.state.form); + }); + } + + prevQuestion = (question) => { + this.setQuestion(this.state.questions[question].prev); + } + setQuestion = (question) => { - this.setState({ question }); + this.setState({ + question, + prevQuestion: Object.keys(this.state.questions).includes(question) ? this.state.questions[question].prev : '', + }, () => { + console.log(this.state.question); + }); } setFuelType = (fuel) => { @@ -293,7 +308,6 @@ class App extends React.Component { tree.input.buildingTypes.includes(this.state.factors.buildingTypes) ) { found = true; - const questions = this.state.questions; questions.buildingTypes.next = tree.output.nextQuestion; questions[tree.output.finalQuestion].next = tree.output.finalPage; @@ -313,6 +327,7 @@ class App extends React.Component { questions.financeInterest.prev = 'buildingTypes'; } + questions.contact.next = "reviewAnswers"; this.setState({ questions }, () => { this.setQuestion(tree.output.nextQuestion); }); @@ -325,114 +340,36 @@ class App extends React.Component { questions.financeInterest.next = 'reviewAnswers'; questions.financeInterest.prev = 'buildingTypes'; questions.reviewAnswers.next = 'thankYou'; + questions.contact.next = "reviewAnswers"; this.setState({ questions }, () => { this.setQuestion('financeInterest'); }); } - const userBuildingId = Object.keys(this.props.buildings.data)[0] === undefined - ? this.state.userBuildingId : Object.keys(this.props.buildings.data)[0]; this.props.onSubmitAnswer({ questionId: 6, answerId: answer, - userBuildingId, + buildingId: this.props.building.data.buildingId, + surveyId: this.props.building.data.surveyId, + userId: this.props.building.data.userId, }); } - newUser = () => { - this.setState({ - isNewUser: !this.state.isNewUser, - isReturningUser: false, - showUserAlert: false, - showTokenAlert: false, - }) - } - - returningUser = () => { - this.setState({ - isReturningUser: !this.state.isReturningUser, - isNewUser: false, - showUserAlert: false, - showTokenAlert: false, - }) - } - - validateInputs() { - if (this.state.isNewUser === true && this.validateNewUser() === false) { - return false; - } - - if (this.state.isReturningUser === true && this.validateReturningUser() === false) { - return false; - } - - return true; - } - - validateNewUser() { - 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})$/; - - 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 (!(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; - } - - validateReturningUser() { - const validEmail = /^$|^[a-zA-Z0-9]+@[a-zA-Z0-9]+\.[A-Za-z]+$/; - if (this.state.form.returningEmail.toString().replace(/^\s+|\s+$/g, '') === '') { - alert('Email address is empty!'); - return false; - } - if (!(validEmail.test(this.state.form.returningEmail))) { - alert('Email address is invalid!'); - return false; - } - return true; - } - - submit = () => { - const userBuildingId = Object.keys(this.props.buildings.data)[0] === undefined - ? this.state.userBuildingId : Object.keys(this.props.buildings.data)[0]; - this.setState({ - submitted: true, - }, () => { - this.props.onSubmitQuestionnaire({ - token: this.state.token, - userBuildingId, - buildingQualified: this.state.buildingQualified, - }); + this.setState({ + submitted: true, + }, () => { + this.props.onSubmitQuestionnaire({ + firstName: this.state.form.firstName, + lastName: this.state.form.lastName, + email: this.state.form.email, + phone: this.state.form.phoneNumber, + surveyId: this.props.building.data.surveyId, + buildingId: this.props.building.data.buildingId, + buildingQualified: this.state.buildingQualified, }); - this.setQuestion(this.state.questions.reviewAnswers.next); + }); + this.setQuestion(this.state.questions.reviewAnswers.next); } handleInputChange = (event) => { @@ -446,44 +383,9 @@ class App extends React.Component { }); } - verifyUser = (userType) => { - let userData = {}; - // New user - if (userType == 1) { - if (this.state.form.EMAIL !== '') { - this.handleSubmit(); - } - userData = { - firstName: this.state.form.FNAME, - lastName: this.state.form.LNAME, - phoneNumber: this.state.form.PHONE, - email: this.state.form.EMAIL, - userType: 1, - }; - } - // Returning user - if (userType == 2) { - userData = { - email: this.state.form.returningEmail, - userType: 2, - }; - } - - if (this.validateInputs()) { - this.props.onVerifyUser(userData); - this.setState({ - showUserAlert: true, - showTokenAlert: false, - }); - } - } - handleSubmit() { const action = process.env.REACT_APP_MAILCHIMP_URL; - const fields = ['FNAME', - 'LNAME', - 'EMAIL', - 'PHONE'] + const fields = ['FNAME', 'LNAME', 'EMAIL', 'PHONE']; const values = fields.map(field => { return `${field}=${encodeURIComponent(this.state.form[field])}`; }).join("&"); @@ -492,385 +394,112 @@ class App extends React.Component { jsonp(url, { param: "c" }); }; - verifyToken = (token) => { - this.props.onVerifyToken({ token: token.toString() }); - this.setState({ - showUserAlert: false, - showTokenAlert: true, - token, - }); - } - - chooseBuilding = (buildingId, questionAnswers, userBuildingId) => { - // Update questions state - this.setState({ - userBuildingId, - questions: this.resetQuestions(), - }, () => { - const questionIds = Object.keys(questionAnswers); - const questions = this.state.questions; - Object.keys(initialQuestions).forEach(question => { - const questionId = initialQuestions[question].id; - if ( - questionId !== undefined && - ( - questionIds.includes(questionId) || - questionIds.includes(questionId.toString()) - ) - ) { - questions[question].value = questionAnswers[questionId]; - if (questions[question].answers !== undefined) { - questions[question].answer = questions[question].answers[questionAnswers[questionId]]; - } else { - questions[question].answer = questionAnswers[questionId]; - } - } - - if (questionId === undefined && - questions[question].questions !== undefined && - Array.isArray(questions[question].questions) === true) { - questions[question].questions.forEach((question_, id) => { - if ( - questionIds.includes(question_.id) || - questionIds.includes(question_.id.toString()) - ) { - questions[question].questions[id].value = questionAnswers[question_.id]; - questions[question].questions[id].answer = questionAnswers[question_.id]; - } - }); - } - }); - - this.setState({ - selectedBuildingId: buildingId, - questions: buildingId === 0 ? initialQuestions : questions, - }, () => { - console.log(this.state.questions); - }); - }); - } - render() { ReactGA.initialize('UA-67611405-11'); ReactGA.pageview('/intake'); - let content = ""; - let alert = ""; - - if (this.props.token.data !== undefined && this.props.token.data !== null && this.props.token.data.success === true) { - const answers = this.props.token.data.data.answers; - const buildings = this.props.token.data.data.buildings; - const user = this.props.token.data.data.user; - const questions = { - addressSearch: , - fuels: , - heatDistribution: , - heatingSystemAge: , - heatingSystemPlan: , - buildingTypes: , - utilityBills: , - comfortIssues: , - financeInterest: , - waitList: , - reviewAnswers: , - }; - - let userBuildingId = 0; - if (this.props.buildings.data !== undefined && Object.keys(this.props.buildings.data).length > 0) { - userBuildingId = Object.keys(this.props.buildings.data)[0]; - } - - if (Object.keys(this.state.pages).includes(this.state.question)) { - content = this.state.pages[this.state.question]; - } else if (Object.keys(questions).includes(this.state.question)) { - content = questions[this.state.question]; - } else if (buildings.length > 0) { - const completedIcon = (
✔ Completed
); - const incompletedIcon = (
⚠ Incomplete
); - - content = ( -
-
- Welcome, {user.firstName} {user.lastName} -
-
- Select a building to continue -
- { - buildings.map(building => { - let buttonStyle = ""; - if (this.state.selectedBuildingId === building.buildingId) { - buttonStyle = "selectedBuildingButton"; - } else { - buttonStyle = "buildingButton"; - } - - if (building.completed === true) { - buttonStyle += " completedButton"; - } else { - buttonStyle += " incompletedButton"; - } - - return ( -
this.chooseBuilding(building.buildingId, building.questions, building.userBuildingId)} - > - { building.completed === true ? completedIcon : incompletedIcon } - {building.address}     -
- ); - }) - } -
this.chooseBuilding(0, {}, 0)} - > - + New Building -
- - - - - -
- ); - } else { - content = questions.addressSearch; - } - } else if (this.props.user.data !== undefined && this.props.user.data !== null && this.props.user.data.success === true) { - content = ( -
-
Successful.
- We have sent you an email, please click on the link in your email to continue the questionniare. -
- ); - } else { - if(this.props.user.loading === true && this.state.showUserAlert == true) { - alert = ( -
- Processing ... -
- ); - } else if (this.props.user.data !== undefined && - this.props.user.data !== null && - this.props.user.data.success === false && - this.state.showUserAlert == true - ) { - alert = ( -
- {this.props.user.data.message} -
- ); - } - - if (this.props.token.data !== undefined && - this.props.token.data !== null && - this.props.token.data.success === false && - this.state.showTokenAlert == true - ) { - alert = ( -
- {this.props.token.data.message} -
- ); - } - let title = ""; - let newUserButton = ""; - let returningUserButton = ""; - let newUser = ""; - let returningUser = ""; - let note = ""; - - if (this.state.isNewUser === false && this.state.isReturningUser === false) { - newUserButton = ( - - ); - returningUserButton = ( - - ); - } - - if (this.state.isNewUser === true || this.state.isReturningUser === true) { - title = ( -
- See if your building qualifies -
- ); - note = ( -
-   {' '} - If you prefer to find out on the phone, call 646-504-2236 -
- ) - } + const building = this.props.building; + // const questions = this.props.questions; + const contact = this.props.contact; + const complete = this.props.complete; - if (this.state.isNewUser === true) { - newUser = ( -
- - - this.handleInputChange(e)} - className="input" - /> - - - - - this.handleInputChange(e)} - className="input" - /> - - - this.handleInputChange(e)} - className="input" - /> - this.handleInputChange(e)} - className="input" - /> - - - -   - -
- ); - } + let addressSearchMessage = ""; + if (building.data !== undefined && building.data.success === false && building.data.message !== undefined) { + addressSearchMessage = this.props.building.data.message; + } - if (this.state.isReturningUser === true) { - returningUser = ( -
- this.handleInputChange(e)} - className="input" - /> - - -
- ); - } + const questions = { + addressSearch: , + fuels: , + heatDistribution: , + heatingSystemAge: , + heatingSystemPlan: , + buildingTypes: , + utilityBills: , + comfortIssues: , + financeInterest: , + waitList: , + contact: , + reviewAnswers: , + }; - content = ( -
- {alert} - {newUserButton}
- {returningUserButton} - {title} - {newUser} - {returningUser} - {note} -
- ) + let content = ""; + if (this.props.building.data !== undefined && this.props.building.data.buildingId !== undefined && Object.keys(this.state.questions).indexOf(this.state.question) > -1) { + content = questions[this.state.question]; + } else if (Object.keys(this.state.pages).indexOf(this.state.question) > -1) { + content = this.state.pages[this.state.question]; + } else { + content = questions[this.state.questions[this.state.question].prev]; } return ( @@ -894,19 +523,19 @@ class App extends React.Component { const mapStatetoProps = state => { return { - user: state.user, - token: state.token, - buildings: state.buildings, + building: state.building, + questions: state.questions, + contact: state.contact, + complete: state.complete, } } const mapDispatchtoProps = dispatch => { return { - onVerifyUser: (userData) => dispatch(verifyUser(userData)), - onVerifyToken: (token) => dispatch(verifyToken(token)), onSubmitBuilding: (data) => dispatch(submitBuilding(data)), onSubmitAnswer: (data) => dispatch(submitAnswer(data)), - onSubmitQuestionnaire: (data) => dispatch(completeQuestionnaire(data)), + onSubmitContact: (data) => dispatch(submitContact(data)), + onSubmitQuestionnaire: (data) => dispatch(submitQuestionnaire(data)), } }; diff --git a/src/BuildingTypes.js b/src/BuildingTypes.js index a41eef050d7a6ca96b3e891453253e1bdac989aa..3acb862538944e5a9d6cab1c24bc16f5e8c74e7d 100644 --- a/src/BuildingTypes.js +++ b/src/BuildingTypes.js @@ -38,7 +38,6 @@ export default class BuildingTypes extends React.Component { ); } - chooseAnswer = (answer) => { this.setState({ answer }); } diff --git a/src/Contact.js b/src/Contact.js new file mode 100644 index 0000000000000000000000000000000000000000..cc7af3497725303e342eb529c6de5c4c61ab3462 --- /dev/null +++ b/src/Contact.js @@ -0,0 +1,151 @@ +import React from "react"; +import { Row, Col, Input, Button } from 'reactstrap'; +import './index.css'; + + +export default class Contact extends React.Component { + constructor(props) { + super(props); + this.state = { + form: this.props.form, + message: '', + }; + } + + handleInputChange = (event) => { + const val = event.target.value; + const name = event.target.name; + this.setState({ + form: { + ...this.state.form, + [name]: val, + }, + }); + } + + prevQuestion = () => { + this.props.prevQuestion('comfortIssues'); + } + + submitContact = () => { + if (this.validateContact()) { + const form = this.state.form; + if (this.state.form.phoneNumber.trim().length === 0) { + form.phoneNumber = 0; + } + this.props.submitContact(form); + } + } + + validateContact() { + 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})$/; + + if (this.state.form.firstName.toString().replace(/^\s+|\s+$/g, '') === '') { + emptyFields.push('First Name'); + } + if (this.state.form.lastName.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.phoneNumber.toString().replace(/^\s+|\s+$/g, '') !== '' && + !(validPhone.test(this.state.form.phoneNumber))) { + invalidFields.push('Phone number'); + } + 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; + } + + render() { + const content = ( +
+ {this.state.message} +
+ Your Information +
+ + + this.handleInputChange(e)} + className="input" + /> + + + this.handleInputChange(e)} + className="input" + /> + + + this.handleInputChange(e)} + className="input" + /> + this.handleInputChange(e)} + className="input" + /> + + + + {'<'} Back + + + + + + +
+ ); + + return ( +
+ {content} +
+ ); + } +} diff --git a/src/ReviewAnswers.js b/src/ReviewAnswers.js index 4f8b6fa823203f4db8d84026572eb20ff5f51fbc..8d1ae31167298ddb02d1cde918e3b236b69b4b35 100644 --- a/src/ReviewAnswers.js +++ b/src/ReviewAnswers.js @@ -23,6 +23,8 @@ export default class ReviewAnswers extends React.Component { render() { const questions = this.props.questions; + const phoneNumber = this.props.form.phoneNumber === 0 ? 'N/A' : this.props.form.phoneNumber; + const content = (
@@ -127,6 +129,28 @@ export default class ReviewAnswers extends React.Component { } }) } + +
+ + +
+ Your Information +
+
+ First Name : {this.props.form.firstName}
+ Last Name : {this.props.form.lastName}
+ Email : {this.props.form.email}
+ Phone Number : {phoneNumber} +
+ +
+ + this.editAnswer('contact') } /> + +
+
+
+
{ - const blocLinkUrl = process.env.REACT_APP_BLOCLINK_URL; - ReactGA.event({ - category: 'Call to Action', - action: 'Button', - label: 'Verify User' - }); - return (dispatch) => { - dispatch({ type: "UserRequested" }); - return axios.put( - `${blocLinkUrl}/buildings/bis/verify-user/`, - user, - { headers : { 'Content-Type' : 'application/x-www-form-urlencoded; charset=UTF-8' } }, - ) - .then(res => dispatch( - { type: "UserSucceeded", data: res })) - .catch(err => dispatch( - { type: "UserFailed", msg: err.response.data })); - } -} - -// asynchronous action creator -export const verifyToken = (user) => { - const blocLinkUrl = process.env.REACT_APP_BLOCLINK_URL; - ReactGA.event({ - category: 'Call to Action', - action: 'Link', - label: 'Verify Token' - }); - return (dispatch) => { - dispatch({ type: "TokenRequested" }); - return axios.put( - `${blocLinkUrl}/buildings/bis/verify-token/`, - user, - { headers : { 'Content-Type' : 'application/x-www-form-urlencoded; charset=UTF-8' } }, - ) - .then(res => dispatch( - { type: "TokenSucceeded", data: res })) - .catch(err => dispatch( - { type: "TokenFailed", msg: err.response.data })); - } -} // asynchronous action creator export const submitBuilding = (data) => { @@ -95,7 +52,29 @@ export const submitAnswer = (data) => { } } -export const completeQuestionnaire = (user) => { +// asynchronous action creator +export const submitContact = (contact) => { + const blocLinkUrl = process.env.REACT_APP_BLOCLINK_URL; + ReactGA.event({ + category: 'Call to Action', + action: 'Button', + label: 'Submit Contact' + }); + return (dispatch) => { + dispatch({ type: "ContactRequested" }); + return axios.put( + `${blocLinkUrl}/buildings/bis/submit-contact/`, + contact, + { headers : { 'Content-Type' : 'application/x-www-form-urlencoded; charset=UTF-8' } }, + ) + .then(res => dispatch( + { type: "ContactSucceeded", data: res })) + .catch(err => dispatch( + { type: "ContactFailed", msg: err.response.data })); + } +} + +export const submitQuestionnaire = (data) => { ReactGA.event({ category: 'Call to Action', action: 'Button', @@ -105,8 +84,8 @@ export const completeQuestionnaire = (user) => { return (dispatch) => { dispatch({ type: "CompleteRequested" }); return axios.put( - `${blocLinkUrl}/buildings/bis/complete/`, - user, + `${blocLinkUrl}/buildings/bis/submit-questionnaire/`, + data, { headers : { 'Content-Type' : 'application/x-www-form-urlencoded; charset=UTF-8' } }, ) .then(res => dispatch( diff --git a/src/constants.js b/src/constants.js index 22ee8771567d2d80d09b4baee9258683830624ff..d8ef6f1fcee2bb7de89a90989c91a5a88c10c0c8 100644 --- a/src/constants.js +++ b/src/constants.js @@ -9,7 +9,7 @@ const questionTree = [ }, output: { nextQuestion: 'financeInterest', - finalQuestion: 'reviewAnswers', + finalQuestion: 'contact', finalPage: 'congrats', }, }, @@ -23,7 +23,7 @@ const questionTree = [ }, output: { nextQuestion: 'utilityBills', - finalQuestion: 'reviewAnswers', + finalQuestion: 'contact', finalPage: 'congrats', }, }, @@ -37,7 +37,7 @@ const questionTree = [ }, output: { nextQuestion: 'waitList', - finalQuestion: 'reviewAnswers', + finalQuestion: 'contact', finalPage: 'thankYou', }, }, @@ -51,7 +51,7 @@ const questionTree = [ }, output: { nextQuestion: 'financeInterest', - finalQuestion: 'reviewAnswers', + finalQuestion: 'contact', finalPage: 'thankYou', }, }, @@ -65,7 +65,7 @@ const questionTree = [ }, output: { nextQuestion: 'financeInterest', - finalQuestion: 'reviewAnswers', + finalQuestion: 'contact', finalPage: 'congrats', }, }, @@ -79,7 +79,7 @@ const questionTree = [ }, output: { nextQuestion: 'utilityBills', - finalQuestion: 'reviewAnswers', + finalQuestion: 'contact', finalPage: 'congrats', }, }, @@ -93,7 +93,7 @@ const questionTree = [ }, output: { nextQuestion: 'financeInterest', - finalQuestion: 'reviewAnswers', + finalQuestion: 'contact', finalPage: 'congrats', }, }, @@ -192,7 +192,7 @@ const initialQuestions = { value: 0, answerIds: [1, 2], prev: '', - next: 'reviewAnswers', + next: 'contact', }, waitList: { id: 10, @@ -201,6 +201,10 @@ const initialQuestions = { value: 0, answerIds: [1, 2], prev: 'buildingTypes', + next: 'contact', + }, + contact: { + prev: '', next: 'reviewAnswers', }, reviewAnswers: { diff --git a/src/reducers/reducer.js b/src/reducers/reducer.js index d5bc6bddc8c6af7e34840506a14674654f563cab..459b690805a7e806dbf8d1cc2ee7d7726c299be0 100644 --- a/src/reducers/reducer.js +++ b/src/reducers/reducer.js @@ -1,17 +1,17 @@ const intialState = { - user: { + building: { loading: false, - data: null, + data: {}, error: false, }, - token: { + questions: { loading: false, - data: null, + data: {}, error: false, }, - buildings: { + contact: { loading: false, - data: {}, + data: null, error: false, }, complete: { @@ -23,77 +23,47 @@ const intialState = { const reducer = (state = intialState, action) => { switch (action.type) { - case "UserRequested": + case "ContactRequested": return { ...state, - user: { - ...state.user, + contact: { + ...state.contact, loading: true, error: false, }, }; - case "UserSucceeded": + case "ContactSucceeded": return { ...state, - user: { + contact: { data: action.data.data, loading: false, error: false, }, }; - case "UserFailed": + case "ContactFailed": return { ...state, - user: { + contact: { data: action.msg, loading: false, error: true, }, }; - case "TokenRequested": - return { - ...state, - token: { - ...state.token, - loading: true, - error: false, - }, - }; - case "TokenSucceeded": - return { - ...state, - token: { - data: action.data.data, - loading: false, - error: false, - }, - }; - case "TokenFailed": - return { - ...state, - token: { - data: action.msg, - loading: false, - error: true, - }, - }; case "BuildingRequested": return { ...state, - buildings: { - ...state.buildings, + building: { + ...state.building, loading: true, error: false, }, }; case "BuildingSucceeded": { - const userBuildingId = action.data.data.data.userBuildingId; - const buildings = state.buildings; - buildings.data[userBuildingId] = {}; return { ...state, - buildings: { - data: buildings.data, + building: { + data: action.data.data.data, loading: false, error: false, }, @@ -102,7 +72,7 @@ const reducer = (state = intialState, action) => { case "BuildingFailed": return { ...state, - buildings: { + building: { data: action.msg, loading: false, error: true, @@ -180,7 +150,6 @@ const reducer = (state = intialState, action) => { }, }; case "CompleteFailed": - console.log('CompleteFailed'); return { ...state, complete: {