aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorArmand Philippot <git@armandphilippot.com>2022-02-10 16:51:08 +0100
committerArmand Philippot <git@armandphilippot.com>2022-02-10 16:51:08 +0100
commit82e387d33fc296b1e5a08fef17bcd4595e0c7071 (patch)
tree780a3886a5e33804b16a335b37bd14bcaa1f6ed3
parentf2be002df3b13254a5b549dd1589089545c53f02 (diff)
refactor: extract contact form from contact page
The contact page file was too long. By extracting the contact form the readability is improved.
-rw-r--r--src/components/ContactForm/ContactForm.module.scss21
-rw-r--r--src/components/ContactForm/ContactForm.tsx155
-rw-r--r--src/i18n/en.json62
-rw-r--r--src/i18n/fr.json62
-rw-r--r--src/pages/contact.tsx143
-rw-r--r--src/styles/pages/Page.module.scss20
6 files changed, 240 insertions, 223 deletions
diff --git a/src/components/ContactForm/ContactForm.module.scss b/src/components/ContactForm/ContactForm.module.scss
new file mode 100644
index 0000000..3f0e861
--- /dev/null
+++ b/src/components/ContactForm/ContactForm.module.scss
@@ -0,0 +1,21 @@
+@use "@styles/abstracts/functions" as fun;
+
+.status {
+ max-width: max-content;
+ margin: var(--spacing-md) 0;
+ padding: var(--spacing-sm);
+ border: fun.convert-px(3) solid var(--color-border-light);
+ border-radius: fun.convert-px(5);
+
+ &--error {
+ border-color: var(--color-token-red);
+ }
+
+ &--success {
+ border-color: var(--color-token-green);
+ }
+
+ &--warning {
+ border-color: var(--color-token-orange);
+ }
+}
diff --git a/src/components/ContactForm/ContactForm.tsx b/src/components/ContactForm/ContactForm.tsx
new file mode 100644
index 0000000..6ab1e2b
--- /dev/null
+++ b/src/components/ContactForm/ContactForm.tsx
@@ -0,0 +1,155 @@
+import { ButtonSubmit } from '@components/Buttons';
+import { Form, FormItem, Input, TextArea } from '@components/Form';
+import { sendMail } from '@services/graphql/mutations';
+import { settings } from '@utils/config';
+import { FormEvent, useState } from 'react';
+import { useIntl } from 'react-intl';
+import styles from './ContactForm.module.scss';
+
+type Status = 'success' | 'error' | 'warning';
+
+const ContactForm = () => {
+ const intl = useIntl();
+ const [name, setName] = useState('');
+ const [email, setEmail] = useState('');
+ const [subject, setSubject] = useState('');
+ const [message, setMessage] = useState('');
+ const [status, setStatus] = useState<Status>();
+ const [statusMessage, setStatusMessage] = useState<string>('');
+
+ const resetForm = () => {
+ setName('');
+ setEmail('');
+ setSubject('');
+ setMessage('');
+ };
+
+ const submitHandler = async (e: FormEvent) => {
+ e.preventDefault();
+
+ if (!name || !email || !message) {
+ setStatus('warning');
+ setStatusMessage(
+ intl.formatMessage({
+ defaultMessage:
+ 'Warning: mail not sent. Some required fields are empty.',
+ description: 'ContactForm: missing fields message.',
+ })
+ );
+ return;
+ }
+
+ const messageHTML = message.replace(/\r?\n/g, '<br />');
+ const body = `Message received from ${name} <${email}> on ${settings.url}.<br /><br />${messageHTML}`;
+ const replyTo = `${name} <${email}>`;
+ const data = {
+ body,
+ mutationId: 'contact',
+ replyTo,
+ subject,
+ };
+ const mail = await sendMail(data);
+
+ if (mail.sent) {
+ setStatus('success');
+ setStatusMessage(
+ intl.formatMessage({
+ defaultMessage:
+ 'Thanks. Your message was successfully sent. I will answer it as soon as possible.',
+ description: 'ContactForm: success message',
+ })
+ );
+ resetForm();
+ } else {
+ const errorPrefix = intl.formatMessage({
+ defaultMessage: 'An error occurred:',
+ description: 'ContactForm: error message',
+ });
+ const error = `${errorPrefix} ${mail.message}`;
+ setStatus('error');
+ setStatusMessage(error);
+ }
+ };
+
+ const getStatus = () => {
+ if (!status) return <></>;
+
+ const statusModifier = `status--${status}`;
+
+ return (
+ <p className={`${styles.status} ${styles[statusModifier]}`}>
+ {statusMessage}
+ </p>
+ );
+ };
+
+ return (
+ <>
+ <Form submitHandler={submitHandler}>
+ <FormItem>
+ <Input
+ id="contact-name"
+ name="name"
+ value={name}
+ setValue={setName}
+ label={intl.formatMessage({
+ defaultMessage: 'Name',
+ description: 'ContactForm: name field label',
+ })}
+ required={true}
+ />
+ </FormItem>
+ <FormItem>
+ <Input
+ id="contact-email"
+ type="email"
+ name="email"
+ value={email}
+ setValue={setEmail}
+ label={intl.formatMessage({
+ defaultMessage: 'Email',
+ description: 'ContactForm: email field label',
+ })}
+ required={true}
+ />
+ </FormItem>
+ <FormItem>
+ <Input
+ id="contact-subject"
+ name="subject"
+ value={subject}
+ setValue={setSubject}
+ label={intl.formatMessage({
+ defaultMessage: 'Subject',
+ description: 'ContactForm: subject field label',
+ })}
+ />
+ </FormItem>
+ <FormItem>
+ <TextArea
+ id="contact-message"
+ name="message"
+ value={message}
+ setValue={setMessage}
+ label={intl.formatMessage({
+ defaultMessage: 'Message',
+ description: 'ContactForm: message field label',
+ })}
+ required={true}
+ />
+ </FormItem>
+ <FormItem>
+ <ButtonSubmit>
+ {intl.formatMessage({
+ defaultMessage: 'Send',
+ description: 'ContactForm: send button text',
+ })}
+ </ButtonSubmit>
+ </FormItem>
+ </Form>
+ {getStatus()}
+ </>
+ );
+};
+
+export default ContactForm;
diff --git a/src/i18n/en.json b/src/i18n/en.json
index cec7ca9..015e87c 100644
--- a/src/i18n/en.json
+++ b/src/i18n/en.json
@@ -31,6 +31,10 @@
"defaultMessage": "Failed to load.",
"description": "TopicsList: failed to load text"
},
+ "0zBQpa": {
+ "defaultMessage": "Message",
+ "description": "ContactForm: message field label"
+ },
"16zl9Z": {
"defaultMessage": "You are here:",
"description": "Breadcrumb: You are here prefix"
@@ -55,10 +59,6 @@
"defaultMessage": "Error 404: Page not found - {websiteName}",
"description": "404Page: SEO - Page title"
},
- "43OmTY": {
- "defaultMessage": "Thanks. Your message was successfully sent. I will answer it as soon as possible.",
- "description": "ContactPage: success message"
- },
"48Ww//": {
"defaultMessage": "Page not found.",
"description": "404Page: SEO - Meta description"
@@ -67,6 +67,10 @@
"defaultMessage": "Legal notice - {websiteName}",
"description": "LegalNoticePage: SEO - Page title"
},
+ "6ibqFS": {
+ "defaultMessage": "Name",
+ "description": "ContactForm: name field label"
+ },
"7TbbIk": {
"defaultMessage": "Blog",
"description": "BlogPage: page title"
@@ -155,14 +159,6 @@
"defaultMessage": "Thematics",
"description": "BlogPage: thematics list widget title"
},
- "Hs+q2V": {
- "defaultMessage": "Email",
- "description": "ContactPage: email field label"
- },
- "HvUpaq": {
- "defaultMessage": "Warning: mail not sent. Some required fields are empty.",
- "description": "ContactPage: missing fields message."
- },
"ILRLTq": {
"defaultMessage": "{brandingName} picture",
"description": "Branding: branding name picture."
@@ -179,10 +175,6 @@
"defaultMessage": "Comment",
"description": "CommentForm: Comment field label"
},
- "Ji6xwo": {
- "defaultMessage": "An error occurred:",
- "description": "ContactPage: error message"
- },
"KERk7L": {
"defaultMessage": "Filter by:",
"description": "BlogPage: sidebar title"
@@ -315,6 +307,10 @@
"defaultMessage": "LinkedIn",
"description": "SocialMedia: LinkedIn"
},
+ "Vuryko": {
+ "defaultMessage": "Email",
+ "description": "ContactForm: email field label"
+ },
"W2G95o": {
"defaultMessage": "Comments:",
"description": "PostMeta: comment count label"
@@ -331,10 +327,18 @@
"defaultMessage": "Twitter",
"description": "Sharing: Twitter"
},
+ "WpycgB": {
+ "defaultMessage": "Warning: mail not sent. Some required fields are empty.",
+ "description": "ContactForm: missing fields message."
+ },
"X3PDXO": {
"defaultMessage": "Animations:",
"description": "ReduceMotion: toggle label"
},
+ "X7n7N2": {
+ "defaultMessage": "Send",
+ "description": "ContactForm: send button text"
+ },
"Y1ZdJ6": {
"defaultMessage": "CV Front-end developer - {websiteName}",
"description": "CVPage: SEO - Page title"
@@ -395,10 +399,6 @@
"defaultMessage": "Contact",
"description": "MainNav: contact link"
},
- "cr2fA4": {
- "defaultMessage": "Subject",
- "description": "ContactPage: subject field label"
- },
"csCQQk": {
"defaultMessage": "LinkedIn",
"description": "Sharing: LinkedIn"
@@ -443,6 +443,10 @@
"defaultMessage": "Failed to load.",
"description": "SearchPage: failed to load text"
},
+ "gQKeF+": {
+ "defaultMessage": "Thanks. Your message was successfully sent. I will answer it as soon as possible.",
+ "description": "ContactForm: success message"
+ },
"hKagVG": {
"defaultMessage": "License:",
"description": "ProjectSummary: license label"
@@ -491,10 +495,6 @@
"defaultMessage": "Number of articles loaded out of the total available.",
"description": "PaginationCursor: loaded articles count aria-label"
},
- "mJLflX": {
- "defaultMessage": "Send",
- "description": "ContactPage: send button text"
- },
"mh7tGg": {
"defaultMessage": "{title} preview",
"description": "ProjectSummary: cover alt text"
@@ -519,6 +519,10 @@
"defaultMessage": "Load more?",
"description": "SearchPage: load more text"
},
+ "pTxT7N": {
+ "defaultMessage": "An error occurred:",
+ "description": "ContactForm: error message"
+ },
"q3U6uI": {
"defaultMessage": "Share",
"description": "Sharing: widget title"
@@ -559,9 +563,9 @@
"defaultMessage": "All fields marked with * are required.",
"description": "ContactPage: required fields text"
},
- "uHp568": {
- "defaultMessage": "Name",
- "description": "ContactPage: name field label"
+ "uMURuJ": {
+ "defaultMessage": "Subject",
+ "description": "ContactForm: subject field label"
},
"ureXFw": {
"defaultMessage": "Share on {name}",
@@ -607,10 +611,6 @@
"defaultMessage": "Free",
"description": "HomePage: link to free thematic"
},
- "x6PPlk": {
- "defaultMessage": "Message",
- "description": "ContactPage: message field label"
- },
"xC3Khf": {
"defaultMessage": "Download <link>CV in PDF</link>",
"description": "CVPreview: download as PDF link"
diff --git a/src/i18n/fr.json b/src/i18n/fr.json
index 49066aa..b20a241 100644
--- a/src/i18n/fr.json
+++ b/src/i18n/fr.json
@@ -31,6 +31,10 @@
"defaultMessage": "Échec du chargement.",
"description": "TopicsList: failed to load text"
},
+ "0zBQpa": {
+ "defaultMessage": "Message",
+ "description": "ContactForm: message field label"
+ },
"16zl9Z": {
"defaultMessage": "Vous êtes ici :",
"description": "Breadcrumb: You are here prefix"
@@ -55,10 +59,6 @@
"defaultMessage": "Erreur 404 : Page non trouvée - {websiteName}",
"description": "404Page: SEO - Page title"
},
- "43OmTY": {
- "defaultMessage": "Merci. Votre message a bien été envoyé. J'y répondrai dès que possible.",
- "description": "ContactPage: success message"
- },
"48Ww//": {
"defaultMessage": "Page non trouvée.",
"description": "404Page: SEO - Meta description"
@@ -67,6 +67,10 @@
"defaultMessage": "Mentions légales - {websiteName}",
"description": "LegalNoticePage: SEO - Page title"
},
+ "6ibqFS": {
+ "defaultMessage": "Nom",
+ "description": "ContactForm: name field label"
+ },
"7TbbIk": {
"defaultMessage": "Blog",
"description": "BlogPage: page title"
@@ -155,14 +159,6 @@
"defaultMessage": "Thématiques",
"description": "BlogPage: thematics list widget title"
},
- "Hs+q2V": {
- "defaultMessage": "Email",
- "description": "ContactPage: email field label"
- },
- "HvUpaq": {
- "defaultMessage": "Attention : le mail n'a pas été envoyé. Certains champs requis sont vides.",
- "description": "ContactPage: missing fields message."
- },
"ILRLTq": {
"defaultMessage": "Image de {brandingName}",
"description": "Branding: branding name picture."
@@ -179,10 +175,6 @@
"defaultMessage": "Commentaire",
"description": "CommentForm: Comment field label"
},
- "Ji6xwo": {
- "defaultMessage": "Une erreur est survenue :",
- "description": "ContactPage: error message"
- },
"KERk7L": {
"defaultMessage": "Filtrer par :",
"description": "BlogPage: sidebar title"
@@ -315,6 +307,10 @@
"defaultMessage": "LinkedIn",
"description": "SocialMedia: LinkedIn"
},
+ "Vuryko": {
+ "defaultMessage": "Email",
+ "description": "ContactForm: email field label"
+ },
"W2G95o": {
"defaultMessage": "Commentaires :",
"description": "PostMeta: comment count label"
@@ -331,10 +327,18 @@
"defaultMessage": "Twitter",
"description": "Sharing: Twitter"
},
+ "WpycgB": {
+ "defaultMessage": "Attention : le mail n'a pas été envoyé. Certains champs requis sont vides.",
+ "description": "ContactForm: missing fields message."
+ },
"X3PDXO": {
"defaultMessage": "Animations :",
"description": "ReduceMotion: toggle label"
},
+ "X7n7N2": {
+ "defaultMessage": "Envoyer",
+ "description": "ContactForm: send button text"
+ },
"Y1ZdJ6": {
"defaultMessage": "CV Intégrateur web / Développeur front-end - {websiteName}",
"description": "CVPage: SEO - Page title"
@@ -395,10 +399,6 @@
"defaultMessage": "Contact",
"description": "MainNav: contact link"
},
- "cr2fA4": {
- "defaultMessage": "Sujet",
- "description": "ContactPage: subject field label"
- },
"csCQQk": {
"defaultMessage": "LinkedIn",
"description": "Sharing: LinkedIn"
@@ -443,6 +443,10 @@
"defaultMessage": "Échec du chargement.",
"description": "SearchPage: failed to load text"
},
+ "gQKeF+": {
+ "defaultMessage": "Merci. Votre message a bien été envoyé. J'y répondrai dès que possible.",
+ "description": "ContactForm: success message"
+ },
"hKagVG": {
"defaultMessage": "Licence :",
"description": "ProjectSummary: license label"
@@ -491,10 +495,6 @@
"defaultMessage": "Nombre d'articles chargés sur le total disponible.",
"description": "PaginationCursor: loaded articles count aria-label"
},
- "mJLflX": {
- "defaultMessage": "Envoyer",
- "description": "ContactPage: send button text"
- },
"mh7tGg": {
"defaultMessage": "Aperçu de {title}",
"description": "ProjectSummary: cover alt text"
@@ -519,6 +519,10 @@
"defaultMessage": "En charger plus ?",
"description": "SearchPage: load more text"
},
+ "pTxT7N": {
+ "defaultMessage": "Une erreur est survenue :",
+ "description": "ContactForm: error message"
+ },
"q3U6uI": {
"defaultMessage": "Partager",
"description": "Sharing: widget title"
@@ -559,9 +563,9 @@
"defaultMessage": "Tous les champs marqués avec * sont requis.",
"description": "ContactPage: required fields text"
},
- "uHp568": {
- "defaultMessage": "Nom",
- "description": "ContactPage: name field label"
+ "uMURuJ": {
+ "defaultMessage": "Sujet",
+ "description": "ContactForm: subject field label"
},
"ureXFw": {
"defaultMessage": "Partager via {name}",
@@ -607,10 +611,6 @@
"defaultMessage": "Libre",
"description": "HomePage: link to free thematic"
},
- "x6PPlk": {
- "defaultMessage": "Message",
- "description": "ContactPage: message field label"
- },
"xC3Khf": {
"defaultMessage": "Télécharger le <link>CV au format PDF</link>",
"description": "CVPreview: download as PDF link"
diff --git a/src/pages/contact.tsx b/src/pages/contact.tsx
index dc57981..176d130 100644
--- a/src/pages/contact.tsx
+++ b/src/pages/contact.tsx
@@ -1,10 +1,8 @@
-import { ButtonSubmit } from '@components/Buttons';
-import { Form, FormItem, Input, TextArea } from '@components/Form';
+import ContactForm from '@components/ContactForm/ContactForm';
import { getLayout } from '@components/Layouts/Layout';
import PostHeader from '@components/PostHeader/PostHeader';
import Sidebar from '@components/Sidebar/Sidebar';
import { SocialMedia } from '@components/Widgets';
-import { sendMail } from '@services/graphql/mutations';
import styles from '@styles/pages/Page.module.scss';
import { NextPageWithLayout } from '@ts/types/app';
import { settings } from '@utils/config';
@@ -12,88 +10,13 @@ import { getIntlInstance, loadTranslation } from '@utils/helpers/i18n';
import { GetStaticProps, GetStaticPropsContext } from 'next';
import Head from 'next/head';
import { useRouter } from 'next/router';
-import { FormEvent, useState } from 'react';
import { useIntl } from 'react-intl';
import { ContactPage as ContactPageSchema, Graph, WebPage } from 'schema-dts';
-type Status = 'success' | 'error' | 'warning';
-
const ContactPage: NextPageWithLayout = () => {
const intl = useIntl();
- const [name, setName] = useState('');
- const [email, setEmail] = useState('');
- const [subject, setSubject] = useState('');
- const [message, setMessage] = useState('');
- const [status, setStatus] = useState<Status>();
- const [statusMessage, setStatusMessage] = useState<string>('');
const router = useRouter();
- const resetForm = () => {
- setName('');
- setEmail('');
- setSubject('');
- setMessage('');
- };
-
- const submitHandler = async (e: FormEvent) => {
- e.preventDefault();
-
- if (!name || !email || !message) {
- setStatus('warning');
- setStatusMessage(
- intl.formatMessage({
- defaultMessage:
- 'Warning: mail not sent. Some required fields are empty.',
- description: 'ContactPage: missing fields message.',
- })
- );
- return;
- }
-
- const messageHTML = message.replace(/\r?\n/g, '<br />');
- const body = `Message received from ${name} <${email}> on ${settings.url}.<br /><br />${messageHTML}`;
- const replyTo = `${name} <${email}>`;
- const data = {
- body,
- mutationId: 'contact',
- replyTo,
- subject,
- };
- const mail = await sendMail(data);
-
- if (mail.sent) {
- setStatus('success');
- setStatusMessage(
- intl.formatMessage({
- defaultMessage:
- 'Thanks. Your message was successfully sent. I will answer it as soon as possible.',
- description: 'ContactPage: success message',
- })
- );
- resetForm();
- } else {
- const errorPrefix = intl.formatMessage({
- defaultMessage: 'An error occurred:',
- description: 'ContactPage: error message',
- });
- const error = `${errorPrefix} ${mail.message}`;
- setStatus('error');
- setStatusMessage(error);
- }
- };
-
- const getStatus = () => {
- if (!status) return <></>;
-
- const statusModifier = `status--${status}`;
-
- return (
- <p className={`${styles.status} ${styles[statusModifier]}`}>
- {statusMessage}
- </p>
- );
- };
-
const pageTitle = intl.formatMessage(
{
defaultMessage: 'Contact form - {websiteName}',
@@ -176,69 +99,7 @@ const ContactPage: NextPageWithLayout = () => {
description: 'ContactPage: required fields text',
})}
</p>
- <Form submitHandler={submitHandler}>
- <FormItem>
- <Input
- id="contact-name"
- name="name"
- value={name}
- setValue={setName}
- label={intl.formatMessage({
- defaultMessage: 'Name',
- description: 'ContactPage: name field label',
- })}
- required={true}
- />
- </FormItem>
- <FormItem>
- <Input
- id="contact-email"
- type="email"
- name="email"
- value={email}
- setValue={setEmail}
- label={intl.formatMessage({
- defaultMessage: 'Email',
- description: 'ContactPage: email field label',
- })}
- required={true}
- />
- </FormItem>
- <FormItem>
- <Input
- id="contact-subject"
- name="subject"
- value={subject}
- setValue={setSubject}
- label={intl.formatMessage({
- defaultMessage: 'Subject',
- description: 'ContactPage: subject field label',
- })}
- />
- </FormItem>
- <FormItem>
- <TextArea
- id="contact-message"
- name="message"
- value={message}
- setValue={setMessage}
- label={intl.formatMessage({
- defaultMessage: 'Message',
- description: 'ContactPage: message field label',
- })}
- required={true}
- />
- </FormItem>
- <FormItem>
- <ButtonSubmit>
- {intl.formatMessage({
- defaultMessage: 'Send',
- description: 'ContactPage: send button text',
- })}
- </ButtonSubmit>
- </FormItem>
- </Form>
- {getStatus()}
+ <ContactForm />
</div>
<Sidebar position="right">
<SocialMedia
diff --git a/src/styles/pages/Page.module.scss b/src/styles/pages/Page.module.scss
index 02aecbf..69a1526 100644
--- a/src/styles/pages/Page.module.scss
+++ b/src/styles/pages/Page.module.scss
@@ -41,23 +41,3 @@ li.item {
grid-column: 2;
}
}
-
-.status {
- max-width: max-content;
- margin: var(--spacing-md) 0;
- padding: var(--spacing-sm);
- border: fun.convert-px(3) solid var(--color-border-light);
- border-radius: fun.convert-px(5);
-
- &--error {
- border-color: var(--color-token-red);
- }
-
- &--success {
- border-color: var(--color-token-green);
- }
-
- &--warning {
- border-color: var(--color-token-orange);
- }
-}