Skip to content

Instantly share code, notes, and snippets.

@nzambello
Created December 1, 2020 13:56
Show Gist options
  • Save nzambello/747d5095af884d11f3064c3ded298e01 to your computer and use it in GitHub Desktop.
Save nzambello/747d5095af884d11f3064c3ded298e01 to your computer and use it in GitHub Desktop.
Volto block with a contact form
import React, { useState, useEffect, useReducer } from 'react';
import { defineMessages, useIntl } from 'react-intl';
import { toast } from 'react-toastify';
import { Toast } from '@plone/volto/components';
import { emailNotification } from '@plone/volto/actions';
import { useSelector, useDispatch } from 'react-redux';
import { Form, Button, Message } from 'semantic-ui-react';
import {
GoogleReCaptchaProvider,
GoogleReCaptcha,
} from 'react-google-recaptcha-v3';
const messages = defineMessages({
send: {
id: 'Send',
defaultMessage: 'Send',
},
contactForm: {
id: 'Contact form',
defaultMessage: 'Contact form',
},
name: {
id: 'Name (or nickname)',
defaultMessage: 'Name (or nickname)',
},
email: {
id: 'Email',
defaultMessage: 'Email',
},
emailAddress: {
id: 'Email address',
defaultMessage: 'Email address',
},
subject: {
id: 'Subject',
defaultMessage: 'Subject',
},
subjectPlaceholder: {
id: 'How can we help you?',
defaultMessage: 'How can we help you?',
},
city: {
id: 'City',
defaultMessage: 'City',
},
age: {
id: 'Age',
defaultMessage: 'Age',
},
optional: {
id: 'optional',
defaultMessage: 'optional',
},
message: {
id: 'Message',
defaultMessage: 'Message',
},
privacy: {
id: 'I authorize the processing of personal data',
defaultMessage: 'I authorize the processing of personal data',
},
error: {
id: 'Error',
defaultMessage: 'Error',
},
messageSent: {
id: 'Email sent',
defaultMessage: 'Email sent',
},
success: {
id: 'Success',
defaultMessage: 'Success',
},
});
const FORM_STATES = {
normal: 'normal',
loading: 'loading',
error: 'error',
success: 'success',
};
const initialState = { loading: false, error: null, result: null };
const formStateReducer = (state, action) => {
switch (action.type) {
case FORM_STATES.normal:
return initialState;
case FORM_STATES.loading:
return { loading: true, error: null, result: null };
case FORM_STATES.error:
return { loading: false, error: action.error, result: null };
case FORM_STATES.success:
return { loading: false, error: null, result: action.result };
default:
return initialState;
}
};
/**
* View contactform block class.
* @class View
* @extends Component
*/
const View = () => {
const intl = useIntl();
const [subject, setSubject] = useState('');
const [from, setFrom] = useState('');
const [name, setName] = useState('');
const [city, setCity] = useState('');
const [age, setAge] = useState('');
const [message, setMessage] = useState('');
const [privacyChecked, setPrivacyChecked] = useState(false);
const [recaptchaToken, setRecaptchaToken] = useState(null);
const [formState, setFormState] = useReducer(formStateReducer, initialState);
const submitResults = useSelector(state => state.emailNotification);
const dispatch = useDispatch();
useEffect(() => {
if (submitResults?.loaded) {
toast.success(
<Toast
success
title={intl.formatMessage(messages.success)}
content={intl.formatMessage(messages.messageSent)}
/>,
);
setFormState({
type: FORM_STATES.success,
result: intl.formatMessage(messages.messageSent),
});
} else if (submitResults?.error) {
let errorDescription = `${submitResults.error.status} ${
submitResults.error.message
}- ${JSON.parse(submitResults.error.response?.text ?? {})?.message}`;
toast.error(
<Toast
error
title={intl.formatMessage(messages.error)}
content={errorDescription}
/>,
);
setFormState({ type: FORM_STATES.error, error: errorDescription });
}
}, [submitResults]);
const submit = () => {
dispatch(
emailNotification(
from,
`Nome: ${name}.\nEtà: ${age}\nCittà: ${city}.\n\n${message}`,
name,
subject,
),
);
setFormState({ type: FORM_STATES.loading });
};
return (
<div className="contactform-block">
{formState.error ? (
<Message
negative
size="big"
icon="times circle"
header={intl.formatMessage(messages.error)}
content={formState.error}
/>
) : formState.result ? (
<Message
positive
size="big"
icon="check circle"
header={intl.formatMessage(messages.success)}
content={formState.result}
/>
) : (
<Form
title={intl.formatMessage(messages.contactForm)}
onSubmit={submit}
loading={formState.loading}
>
<Form.Input
fluid
label={intl.formatMessage(messages.name)}
placeholder={intl.formatMessage(messages.name)}
name="name"
id="contact-form-name"
value={name}
onChange={e => setName(e.target.value)}
required
/>
<Form.Input
fluid
type="email"
label={intl.formatMessage(messages.emailAddress)}
placeholder={intl.formatMessage(messages.email)}
name="from"
id="contact-form-from"
value={from}
onChange={e => setFrom(e.target.value)}
required
/>
<Form.Input
fluid
label={intl.formatMessage(messages.subject)}
placeholder={intl.formatMessage(messages.subjectPlaceholder)}
name="subject"
id="contact-form-subject"
value={subject}
onChange={e => setSubject(e.target.value)}
required
/>
<Form.Input
fluid
label={intl.formatMessage(messages.age)}
placeholder={intl.formatMessage(messages.age)}
name="age"
id="contact-form-age"
value={age}
onChange={e => setAge(e.target.value)}
required
/>
<Form.Input
fluid
label={intl.formatMessage(messages.city)}
placeholder={intl.formatMessage(messages.city)}
name="city"
id="contact-form-city"
value={city}
onChange={e => setCity(e.target.value)}
required
/>
<Form.TextArea
label={intl.formatMessage(messages.message)}
placeholder={intl.formatMessage(messages.message)}
name="message"
id="contact-form-message"
value={message}
rows={3}
onChange={e => setMessage(e.target.value)}
required
/>
<Form.Checkbox
label={intl.formatMessage(messages.privacy)}
name="privacyChecked"
id="contact-form-privacychecked"
checked={privacyChecked}
onChange={e => setPrivacyChecked(e.target.checked)}
required
/>
{process.env.RAZZLE_RECAPTCHA_KEY && (
<Form.Field inline>
<GoogleReCaptchaProvider
reCaptchaKey={process.env.RAZZLE_RECAPTCHA_KEY}
language={intl.locale ?? 'it'}
>
<GoogleReCaptcha onVerify={setRecaptchaToken} />
</GoogleReCaptchaProvider>
</Form.Field>
)}
<Button
floated="left"
type="submit"
disabled={
!privacyChecked ||
(!recaptchaToken && process.env.RAZZLE_RECAPTCHA_KEY)
}
>
{intl.formatMessage(messages.send)}
</Button>
</Form>
)}
</div>
);
};
export default View;
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment