diff options
Diffstat (limited to 'src')
| -rw-r--r-- | src/components/ContactForm/ContactForm.module.scss | 21 | ||||
| -rw-r--r-- | src/components/ContactForm/ContactForm.tsx | 155 | ||||
| -rw-r--r-- | src/i18n/en.json | 62 | ||||
| -rw-r--r-- | src/i18n/fr.json | 62 | ||||
| -rw-r--r-- | src/pages/contact.tsx | 143 | ||||
| -rw-r--r-- | src/styles/pages/Page.module.scss | 20 |
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); - } -} |
