From 08650b5440411092cd8d4ef51697fd86e8042293 Mon Sep 17 00:00:00 2001 From: Aizizi Yigaimu Date: Fri, 22 Nov 2019 19:33:13 -0500 Subject: [PATCH 01/11] Refactor code by removing unnecessary logics --- src/AddressSearch.js | 6 ++-- src/App.css | 16 +++++++++- src/actions.js | 71 +++++++++++++++-------------------------- src/reducers/reducer.js | 66 +++++++++++--------------------------- 4 files changed, 62 insertions(+), 97 deletions(-) diff --git a/src/AddressSearch.js b/src/AddressSearch.js index 170855564..66e28066d 100644 --- a/src/AddressSearch.js +++ b/src/AddressSearch.js @@ -75,15 +75,15 @@ export default class AddressSearch extends React.Component {
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 c7fe86bcf..a3310e967 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; @@ -216,7 +230,7 @@ width: 100%; color: #002F43; font-weight: bold; - padding-left: 0; + padding-left: 0; padding-right: 0; font-family: 'AvenirNext'; } diff --git a/src/actions.js b/src/actions.js index c621ab97a..18ecb9b22 100644 --- a/src/actions.js +++ b/src/actions.js @@ -2,49 +2,6 @@ import axios from 'axios'; import ReactGA from 'react-ga'; import { initialQuestions } from './constants'; -// asynchronous action creator -export const verifyUser = (user) => { - 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/reducers/reducer.js b/src/reducers/reducer.js index d5bc6bddc..a592c3510 100644 --- a/src/reducers/reducer.js +++ b/src/reducers/reducer.js @@ -1,15 +1,15 @@ const intialState = { - user: { + contact: { loading: false, data: null, error: false, }, - token: { + building: { loading: false, - data: null, + data: {}, error: false, }, - buildings: { + questions: { loading: false, data: {}, error: false, @@ -23,77 +23,50 @@ 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] = {}; + const buildingId = action.data.data.data.buildingId; + const building = state.building; + building.data[buildingId] = {}; return { ...state, - buildings: { - data: buildings.data, + building: { + data: building.data, loading: false, error: false, }, @@ -102,7 +75,7 @@ const reducer = (state = intialState, action) => { case "BuildingFailed": return { ...state, - buildings: { + building: { data: action.msg, loading: false, error: true, @@ -180,7 +153,6 @@ const reducer = (state = intialState, action) => { }, }; case "CompleteFailed": - console.log('CompleteFailed'); return { ...state, complete: { -- GitLab From 4b78fbaccf1ccbc3326bd16ca00a9812ace596fe Mon Sep 17 00:00:00 2001 From: Aizizi Yigaimu Date: Fri, 22 Nov 2019 20:07:46 -0500 Subject: [PATCH 02/11] Add question logic --- package-lock.json | 82 ++++-- src/App.js | 609 ++++++++-------------------------------- src/reducers/reducer.js | 15 +- 3 files changed, 184 insertions(+), 522 deletions(-) diff --git a/package-lock.json b/package-lock.json index 47bbb4f2d..b12f58fba 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/App.js b/src/App.js index 85a2b2fd7..8df4f099e 100644 --- a/src/App.js +++ b/src/App.js @@ -15,35 +15,28 @@ import Congrats from './Congrats'; import FinanceInterest from './FinanceInterest'; import WaitList from './WaitList'; 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: '', }, factors: { fuel: '', @@ -52,7 +45,7 @@ class App extends React.Component { heatingSystemPlan: '', buildingTypes: '', }, - question: '', + question: 'addressSearch', reviewQuestions: [], questions: this.resetQuestions(), pages: { @@ -176,13 +169,7 @@ class App extends React.Component { }; } - componentDidMount() { - const url = window.location.href; - const token = url.substring(url.lastIndexOf('/') + 1); - if(token !== ''){ - this.verifyToken(token); - } - } + componentDidMount() { } prevQuestion = (question) => { this.setQuestion(this.state.questions[question].prev); @@ -209,17 +196,16 @@ class App extends React.Component { 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]; + const buildingId = Object.keys(this.props.building.data)[0] === undefined + ? this.state.buildingId : Object.keys(this.props.building.data)[0]; Object.keys(questionAnswers).forEach(questionId => { this.props.onSubmitAnswer({ questionId: questionId, answerId: questionAnswers[questionId], - userBuildingId, - // userBuildingId: Object.keys(this.props.buildings.data)[0], + buildingId, + // buildingId: Object.keys(this.props.building.data)[0], }); }); } @@ -330,46 +316,16 @@ class App extends React.Component { }); } - const userBuildingId = Object.keys(this.props.buildings.data)[0] === undefined - ? this.state.userBuildingId : Object.keys(this.props.buildings.data)[0]; + const buildingId = Object.keys(this.props.building.data)[0] === undefined + ? this.state.buildingId : Object.keys(this.props.building.data)[0]; this.props.onSubmitAnswer({ questionId: 6, answerId: answer, - userBuildingId, + buildingId, }); } - 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() { + validateContact() { const emptyFields = []; const invalidFields = []; const errorMessage = []; @@ -406,33 +362,18 @@ class App extends React.Component { 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, - }); + const buildingId = Object.keys(this.props.building.data)[0] === undefined + ? this.state.buildingId : Object.keys(this.props.building.data)[0]; + this.setState({ + submitted: true, + }, () => { + this.props.onSubmitQuestionnaire({ + buildingId, + buildingQualified: this.state.buildingQualified, }); - this.setQuestion(this.state.questions.reviewAnswers.next); + }); + this.setQuestion(this.state.questions.reviewAnswers.next); } handleInputChange = (event) => { @@ -446,44 +387,25 @@ class App extends React.Component { }); } - verifyUser = (userType) => { - let userData = {}; - // New user - if (userType == 1) { + submitContact = () => { + if (this.validateContact()) { + // For MailChamp use if (this.state.form.EMAIL !== '') { this.handleSubmit(); } - userData = { + const contactData = { 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, - }); + this.props.onSubmitContact(contactData); } } 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,386 +414,91 @@ 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 -
- ) - } - - 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" - /> - - - -   - -
- ); - } - - if (this.state.isReturningUser === true) { - returningUser = ( -
- this.handleInputChange(e)} - className="input" - /> - - -
- ); - } - - content = ( -
- {alert} - {newUserButton}
- {returningUserButton} - {title} - {newUser} - {returningUser} - {note} -
- ) - } + const building = this.props.building; + // const questions = this.props.questions; + const contact = this.props.contact; + const complete = this.props.complete; + const questions = { + addressSearch: , + fuels: , + heatDistribution: , + heatingSystemAge: , + heatingSystemPlan: , + buildingTypes: , + utilityBills: , + comfortIssues: , + financeInterest: , + waitList: , + reviewAnswers: , + }; + let content = questions[this.state.question]; return (
@@ -894,19 +521,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/reducers/reducer.js b/src/reducers/reducer.js index a592c3510..459b69080 100644 --- a/src/reducers/reducer.js +++ b/src/reducers/reducer.js @@ -1,9 +1,4 @@ const intialState = { - contact: { - loading: false, - data: null, - error: false, - }, building: { loading: false, data: {}, @@ -14,6 +9,11 @@ const intialState = { data: {}, error: false, }, + contact: { + loading: false, + data: null, + error: false, + }, complete: { loading: false, data: {}, @@ -60,13 +60,10 @@ const reducer = (state = intialState, action) => { }, }; case "BuildingSucceeded": { - const buildingId = action.data.data.data.buildingId; - const building = state.building; - building.data[buildingId] = {}; return { ...state, building: { - data: building.data, + data: action.data.data.data, loading: false, error: false, }, -- GitLab From 89a4cec7a0b953ea1c4f0e36d328eab5b578efeb Mon Sep 17 00:00:00 2001 From: Aizizi Yigaimu Date: Mon, 25 Nov 2019 16:05:35 -0500 Subject: [PATCH 03/11] Add contact page, integrated questions with new design --- src/AddressSearch.js | 12 +++- src/App.css | 11 ++++ src/App.js | 61 ++++++++++++++++----- src/Contact.js | 127 +++++++++++++++++++++++++++++++++++++++++++ src/constants.js | 20 ++++--- 5 files changed, 208 insertions(+), 23 deletions(-) create mode 100644 src/Contact.js diff --git a/src/AddressSearch.js b/src/AddressSearch.js index 66e28066d..f025cec2b 100644 --- a/src/AddressSearch.js +++ b/src/AddressSearch.js @@ -49,7 +49,6 @@ export default class AddressSearch extends React.Component { render() { const {viewport} = this.state - let addressData = ""; if(this.state.addressFound === false){ addressData = ( @@ -67,8 +66,19 @@ export default class AddressSearch extends React.Component { ) } + let message = ""; + if(this.props.message !== '') { + message = ( +
+ Sorry. {this.props.message}! +
+ ); + } + + const content = (
+ {message}
What is your building address?
diff --git a/src/App.css b/src/App.css index a3310e967..53ddbf807 100644 --- a/src/App.css +++ b/src/App.css @@ -145,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; } diff --git a/src/App.js b/src/App.js index 8df4f099e..cbb007229 100644 --- a/src/App.js +++ b/src/App.js @@ -14,6 +14,7 @@ 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 { questionTree } from './constants'; import { connect } from 'react-redux'; @@ -45,6 +46,7 @@ class App extends React.Component { heatingSystemPlan: '', buildingTypes: '', }, + prevQuestion: '', question: 'addressSearch', reviewQuestions: [], questions: this.resetQuestions(), @@ -63,6 +65,7 @@ class App extends React.Component { title: 'What is your building address?', answer: '', value: '', + prev: 'addressSearch', next: 'fuels', }, fuels: { @@ -151,7 +154,7 @@ class App extends React.Component { value: 0, answerIds: [1, 2], prev: '', - next: 'reviewAnswers', + next: 'contact', }, waitList: { id: 10, @@ -160,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: { @@ -171,10 +179,6 @@ class App extends React.Component { componentDidMount() { } - prevQuestion = (question) => { - this.setQuestion(this.state.questions[question].prev); - } - nextQuestion = (question, answer, value) => { const questionAnswers = {}; const questions = this.state.questions; @@ -198,22 +202,34 @@ class App extends React.Component { address: this.state.questions[question].value, }); } else { - const buildingId = Object.keys(this.props.building.data)[0] === undefined - ? this.state.buildingId : Object.keys(this.props.building.data)[0]; Object.keys(questionAnswers).forEach(questionId => { this.props.onSubmitAnswer({ questionId: questionId, answerId: questionAnswers[questionId], - buildingId, - // buildingId: Object.keys(this.props.building.data)[0], + buildingId: this.props.building.data.buildingId, + surveyId: this.props.building.data.surveyId, + uuid: this.props.building.data.uuid, }); }); } }); } + prevQuestion = (question) => { + this.setQuestion(this.state.questions[question].prev); + } + setQuestion = (question) => { - this.setState({ question }); + console.log(this.state.question); + console.log(this.state.questions); + console.log(this.state.questions[question]); + console.log(this.state.questions[question].prev); + this.setState({ + question, + prevQuestion: this.state.questions[question].prev, + }, () => { + console.log(this.state.question); + }); } setFuelType = (fuel) => { @@ -316,12 +332,12 @@ class App extends React.Component { }); } - const buildingId = Object.keys(this.props.building.data)[0] === undefined - ? this.state.buildingId : Object.keys(this.props.building.data)[0]; this.props.onSubmitAnswer({ questionId: 6, answerId: answer, - buildingId, + buildingId: this.props.building.data.buildingId, + surveyId: this.props.building.data.surveyId, + uuid: this.props.building.data.uuid, }); } @@ -422,8 +438,15 @@ class App extends React.Component { // const questions = this.props.questions; const contact = this.props.contact; const complete = this.props.complete; + + let addressSearchMessage = ""; + if (building.data !== undefined && building.data.success === false && building.data.message !== undefined) { + addressSearchMessage = this.props.building.data.message; + } + const questions = { addressSearch: , @@ -492,13 +515,23 @@ class App extends React.Component { answer={this.state.questions['waitList'].value} answerIds={this.state.questions['waitList'].answerIds} />, + contact: , reviewAnswers: , }; - let content = questions[this.state.question]; + + let content = ""; + if (this.props.building.data !== undefined && this.props.building.data.buildingId !== undefined) { + content = questions[this.state.question]; + } else { + content = questions[this.state.questions[this.state.question].prev]; + } return (
diff --git a/src/Contact.js b/src/Contact.js new file mode 100644 index 000000000..afa97b94e --- /dev/null +++ b/src/Contact.js @@ -0,0 +1,127 @@ +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: { + FNAME: '', + LNAME: '', + EMAIL: '', + PHONE: '', + }, + 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'); + } + + nextQuestion = () => { + if (this.state.form.FNAME.length === 0 || + this.state.form.LNAME.length === 0 || + this.state.form.EMAIL.length === 0 || + this.state.form.PHONE.length === 0) { + this.setState({ + message: + ( +
+ Please fill out all the information +
+ ) + }); + }else { + this.props.nextQuestion('reviewAnswers'); + } + } + + 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/constants.js b/src/constants.js index 22ee87715..d8ef6f1fc 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: { -- GitLab From fd0b87ff1f8b34fbb2ebe3d2427829d263a8a385 Mon Sep 17 00:00:00 2001 From: Aizizi Yigaimu Date: Mon, 25 Nov 2019 16:55:14 -0500 Subject: [PATCH 04/11] Updates --- src/App.js | 52 +++++++++++++++++++++++++++++++++----------------- src/Contact.js | 6 +++--- 2 files changed, 37 insertions(+), 21 deletions(-) diff --git a/src/App.js b/src/App.js index cbb007229..a02c2c52e 100644 --- a/src/App.js +++ b/src/App.js @@ -208,13 +208,29 @@ class App extends React.Component { answerId: questionAnswers[questionId], buildingId: this.props.building.data.buildingId, surveyId: this.props.building.data.surveyId, - uuid: this.props.building.data.uuid, + userId: this.props.building.data.userId, }); }); } }); } + submitContact = (contact) => { + console.log(contact); + console.log(this.state.questions['contact']); + console.log(this.state.questions['contact'].next); + this.setQuestion(this.state.questions['contact'].next); + // contactData = { + // firstName: contact.firstName, + // lastName: contact.lastName, + // phoneNumber: contact.phoneNumber, + // email: contact.email, + // buildingId: this.props.building.data.buildingId, + // }); + + this.props.onSubmitContact(contact); + } + prevQuestion = (question) => { this.setQuestion(this.state.questions[question].prev); } @@ -337,7 +353,7 @@ class App extends React.Component { answerId: answer, buildingId: this.props.building.data.buildingId, surveyId: this.props.building.data.surveyId, - uuid: this.props.building.data.uuid, + userId: this.props.building.data.userId, }); } @@ -403,21 +419,21 @@ class App extends React.Component { }); } - submitContact = () => { - if (this.validateContact()) { - // For MailChamp use - if (this.state.form.EMAIL !== '') { - this.handleSubmit(); - } - const contactData = { - firstName: this.state.form.FNAME, - lastName: this.state.form.LNAME, - phoneNumber: this.state.form.PHONE, - email: this.state.form.EMAIL, - }; - this.props.onSubmitContact(contactData); - } - } + // submitContact = () => { + // if (this.validateContact()) { + // // For MailChamp use + // if (this.state.form.EMAIL !== '') { + // this.handleSubmit(); + // } + // const contactData = { + // firstName: this.state.form.FNAME, + // lastName: this.state.form.LNAME, + // phoneNumber: this.state.form.PHONE, + // email: this.state.form.EMAIL, + // }; + // this.props.onSubmitContact(contactData); + // } + // } handleSubmit() { const action = process.env.REACT_APP_MAILCHIMP_URL; @@ -517,7 +533,7 @@ class App extends React.Component { />, contact: , reviewAnswers: { + submitContact = () => { if (this.state.form.FNAME.length === 0 || this.state.form.LNAME.length === 0 || this.state.form.EMAIL.length === 0 || @@ -46,7 +46,7 @@ export default class Contact extends React.Component { ) }); }else { - this.props.nextQuestion('reviewAnswers'); + this.props.submitContact(this.state.form); } } @@ -103,7 +103,7 @@ export default class Contact extends React.Component {
Date: Wed, 27 Nov 2019 11:43:05 -0500 Subject: [PATCH 09/11] fix contact page not loading issue --- src/App.js | 1 + src/BuildingTypes.js | 1 - 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/src/App.js b/src/App.js index d30e72f0c..48c35f9ad 100644 --- a/src/App.js +++ b/src/App.js @@ -195,6 +195,7 @@ 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') { diff --git a/src/BuildingTypes.js b/src/BuildingTypes.js index a41eef050..3acb86253 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 }); } -- GitLab From 32db8df0d349978ec5bcd6d56060086c4ddfb1ce Mon Sep 17 00:00:00 2001 From: Aizizi Yigaimu Date: Wed, 27 Nov 2019 11:46:14 -0500 Subject: [PATCH 10/11] Fix phone number optional check issue --- src/Contact.js | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/Contact.js b/src/Contact.js index e3b3b1f46..a91d0de52 100644 --- a/src/Contact.js +++ b/src/Contact.js @@ -30,8 +30,7 @@ export default class Contact extends React.Component { submitContact = () => { if (this.state.form.firstName.length === 0 || this.state.form.lastName.length === 0 || - this.state.form.email.length === 0 || - this.state.form.phoneNumber.length === 0) { + this.state.form.email.length === 0) { this.setState({ message: ( -- GitLab From f088e3d291881c586d5f5aa2b055857b1e80a255 Mon Sep 17 00:00:00 2001 From: Aizizi Yigaimu Date: Wed, 27 Nov 2019 13:36:50 -0500 Subject: [PATCH 11/11] Fix submit questionnaire issue --- src/App.js | 72 ++++++++------------------------------------ src/Contact.js | 56 ++++++++++++++++++++++++++-------- src/ReviewAnswers.js | 4 ++- 3 files changed, 58 insertions(+), 74 deletions(-) diff --git a/src/App.js b/src/App.js index 48c35f9ad..38bae9f26 100644 --- a/src/App.js +++ b/src/App.js @@ -207,8 +207,8 @@ class App extends React.Component { this.props.onSubmitAnswer({ questionId: questionId, answerId: questionAnswers[questionId], - buildingId: this.props.building.data.buildingId, surveyId: this.props.building.data.surveyId, + buildingId: this.props.building.data.buildingId, userId: this.props.building.data.userId, }); }); @@ -224,8 +224,8 @@ class App extends React.Component { lastName: contact.lastName, phoneNumber: contact.phoneNumber, email: contact.email, - userId: this.props.building.data.userId, buildingId: this.props.building.data.buildingId, + userId: this.props.building.data.userId, }, }, () => { this.props.onSubmitContact(this.state.form); @@ -239,7 +239,7 @@ class App extends React.Component { setQuestion = (question) => { this.setState({ question, - prevQuestion: this.state.questions[question].prev, + prevQuestion: Object.keys(this.state.questions).includes(question) ? this.state.questions[question].prev : '', }, () => { console.log(this.state.question); }); @@ -355,51 +355,17 @@ class App extends React.Component { }); } - 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; - } - submit = () => { - const buildingId = Object.keys(this.props.building.data)[0] === undefined - ? this.state.buildingId : Object.keys(this.props.building.data)[0]; this.setState({ submitted: true, }, () => { this.props.onSubmitQuestionnaire({ - buildingId, + 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, }); }); @@ -417,22 +383,6 @@ class App extends React.Component { }); } - // submitContact = () => { - // if (this.validateContact()) { - // // For MailChamp use - // if (this.state.form.EMAIL !== '') { - // this.handleSubmit(); - // } - // const contactData = { - // firstName: this.state.form.FNAME, - // lastName: this.state.form.LNAME, - // phoneNumber: this.state.form.PHONE, - // email: this.state.form.EMAIL, - // }; - // this.props.onSubmitContact(contactData); - // } - // } - handleSubmit() { const action = process.env.REACT_APP_MAILCHIMP_URL; const fields = ['FNAME', 'LNAME', 'EMAIL', 'PHONE']; @@ -544,8 +494,10 @@ class App extends React.Component { }; let content = ""; - if (this.props.building.data !== undefined && this.props.building.data.buildingId !== undefined) { + 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]; } diff --git a/src/Contact.js b/src/Contact.js index a91d0de52..cc7af3497 100644 --- a/src/Contact.js +++ b/src/Contact.js @@ -28,22 +28,52 @@ export default class Contact extends React.Component { } submitContact = () => { - if (this.state.form.firstName.length === 0 || - this.state.form.lastName.length === 0 || - this.state.form.email.length === 0) { - this.setState({ - message: - ( -
- Please fill out all the information -
- ) - }); - }else { - this.props.submitContact(this.state.form); + 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 = (
diff --git a/src/ReviewAnswers.js b/src/ReviewAnswers.js index 4508729b2..8d1ae3116 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 = (
@@ -138,7 +140,7 @@ export default class ReviewAnswers extends React.Component { First Name : {this.props.form.firstName}
Last Name : {this.props.form.lastName}
Email : {this.props.form.email}
- Phone Number : {this.props.form.phoneNumber} + Phone Number : {phoneNumber}
-- GitLab