From f3c8cc95e2be4eec2fa1dc41e805890eb9d7e873 Mon Sep 17 00:00:00 2001 From: Edwin J Date: Mon, 22 Aug 2022 21:28:20 -0700 Subject: [PATCH 01/37] added sendContactData function and its entry into rootSaga in /client/redux/sagas/data.js --- client/redux/sagas/data.js | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/client/redux/sagas/data.js b/client/redux/sagas/data.js index e911ebdc7..90e6d84ac 100644 --- a/client/redux/sagas/data.js +++ b/client/redux/sagas/data.js @@ -145,8 +145,21 @@ function* getNcByLngLat(action) { } } +function* sendContactData(action) { + try { + const message = action.payload; + const data = yield call(postFeedback, message); + yield put(gitResponseSuccess(data)); + yield put(showFeedbackSuccess(true)); + } catch (e) { + yield put(gitResponseFailure(e)); + yield put(setErrorModal(true)); + } +} + export default function* rootSaga() { yield takeLatest(mapFiltersTypes.UPDATE_MAP_DATE_RANGE, getMapData); yield takeLatest(types.GET_NC_BY_LNG_LAT, getNcByLngLat) yield takeEvery(types.GET_PIN_INFO_REQUEST, getPinData); + yield takeLatest(types.SEND_GIT_REQUEST, sendContactData); } From 2f6f6d20a5f354b4bcb455525ac8550dc3d693bc Mon Sep 17 00:00:00 2001 From: Edwin J Date: Tue, 23 Aug 2022 01:53:13 -0700 Subject: [PATCH 02/37] refactored /client/components/main/ContactForm.jsx from class to function components and react hooks. applied dependent code from v1 into /client/redux/sagas/data.js. confirmed submission works and reaches 311-data github support. --- client/components/main/ContactForm.jsx | 342 ++++++++++++++++++------- client/redux/sagas/data.js | 12 + 2 files changed, 266 insertions(+), 88 deletions(-) diff --git a/client/components/main/ContactForm.jsx b/client/components/main/ContactForm.jsx index d0dad2dae..6ae66f3ce 100644 --- a/client/components/main/ContactForm.jsx +++ b/client/components/main/ContactForm.jsx @@ -1,98 +1,264 @@ -import React from 'react'; -import { useForm } from '@formspree/react'; -import { - makeStyles, - Container, - Grid, - Button, -} from '@material-ui/core'; - -const useStyles = makeStyles({ - root: { - color: 'black', - backgroundColor: 'white', - minHeight: '40em', - padding: '2em', - '& h1': { - fontSize: '2.5em', - }, - '& img': { - maxWidth: '100%', - height: 'auto', - display: 'block', - marginLeft: 'auto', - marginRight: 'auto', - }, - '& label': { - marginTop: '1em', - fontWeight: 500, - display: 'block', - width: '20em', - }, - '& input': { - width: '40em', - padding: '0.5em', - }, - '& textarea': { - width: '40em', - padding: '0.5em', +import React, { useState, useEffect, useCallback } from 'react'; +import { useDispatch } from 'react-redux'; +import { sendGitRequest } from '@reducers/data'; +import clx from 'classnames'; + +// makeStyles from v1 +// import { +// makeStyles, +// } from '@material-ui/core'; +// +// const useStyles = makeStyles({ +// root: { +// color: 'black', +// backgroundColor: 'white', +// minHeight: '40em', +// padding: '2em', +// '& h1': { +// fontSize: '2.5em', +// }, +// '& img': { +// maxWidth: '100%', +// height: 'auto', +// display: 'block', +// marginLeft: 'auto', +// marginRight: 'auto', +// }, +// '& label': { +// marginTop: '1em', +// fontWeight: 500, +// display: 'block', +// width: '20em', +// }, +// '& input': { +// width: '40em', +// padding: '0.5em', +// }, +// '& textarea': { +// width: '40em', +// padding: '0.5em', +// }, +// }, +// }); + +const contactInitialState = { + firstName: '', + lastName: '', + email: '', + association: '', + message: '', + errors: { + missingFirstName: false, + missingLastName: false, + missingEmail: false, + invalidEmail: false, + missingMessage: false, + }, + loading: false, +}; + +const ContactForm = () => { + /* Note: the `useState` declaration below uses destructuring syntax to initialize individual + * state variables. each variable (e.g. - firstName, missingFirstName, etc) will contain initial + * values as per each key:value defined in the contactInitialState` object above. each value will + * become globally accessible variable within the ContactForm component + */ + const [{ + firstName, + lastName, + email, + association, + message, + errors: { + missingFirstName, + missingLastName, + missingEmail, + invalidEmail, + missingMessage, }, + loading, }, -}); + setState] = useState(contactInitialState); -const FORMSPREE_FORM_ID = 'xknkwwez'; + function clearFields() { + setState({ ...contactInitialState }); + } -const ContactForm = () => { - const [state, handleSubmit] = useForm(FORMSPREE_FORM_ID); - const classes = useStyles(); + useEffect(() => { + clearFields(); + }, []); + + function validateEmail(emailAddress) { + // eslint-disable-next-line + if (/^\w+([\.-]?\w+)*@\w+([\.-]?\w+)*(\.\w{2,3})+$/.test(emailAddress)) { + return true; + } + return false; + } - React.useEffect(() => { - console.log(state); - }); + function validateForm() { + const noFirstName = firstName.trim().length === 0; + const noLastName = lastName.trim().length === 0; + const noEmail = email.trim().length === 0; + const noMessage = message.trim().length === 0; + const incompleteEmail = (!noEmail && !validateEmail(email)); + if (!noFirstName && !noLastName && !noEmail && !noMessage && !incompleteEmail) { + return true; + } + + setState(prevState => ({ + ...prevState, + ...{ + errors: { + missingFirstName: noFirstName, + missingLastName: noLastName, + missingEmail: noEmail, + invalidEmail: incompleteEmail, + missingMessage: noMessage, + }, + }, + })); + return false; + } + + function onInputChange(event) { + event.preventDefault(); + const { name, value } = event.target; + setState(prevState => ({ ...prevState, [name]: value })); + } + + // define a method, `callSendGitRequest`, to dispatch an action to redux using `useDipatch()` hook + // (notice there is no need to use `connect` or `mapStateToProps` anymore) + const dispatch = useDispatch(); + const callSendGitRequest = useCallback(obj => dispatch(sendGitRequest(obj)), [dispatch]); + + function handleSubmit(event) { + event.preventDefault(); + + if (validateForm()) { + const body = [ + `First name: ${firstName}`, + `Last name: ${lastName}`, + `Email: ${email}`, + `Association: ${association || 'Not provided'}`, + `Message: ${message}`, + ].join('\n'); + + callSendGitRequest({ title: email, body }); // dispatch action to redux with payload + + setState(prevState => ({ ...prevState, ...{ loading: true } })); + } + } return ( - - - -

Contact Us

- { state.succeeded - && ( -
- Thanks for contacting us! We will get back to you in 2-3 business days. -
- )} - { !state.succeeded - && ( - <> -
- Don't See What You Need? - We want to build a tool that works for you. - We are open to suggestions and feedback and would love the opportunity - to get connected. - Feel free to input your information in the contact form below - and we will be sure to get back to you within 2-3 business days. -
-
-
- - -
-
- - -
-
- -