From 70efcfeaa0603415dd992cb662d8efb960e6e49a Mon Sep 17 00:00:00 2001
From: Armand Philippot
Date: Tue, 26 Sep 2023 15:54:28 +0200
Subject: refactor(routes): replace hardcoded routes with constants
It makes it easier to change a route if needed and it avoid typo
mistakes.
I also refactored a bit the concerned files to be complient with the
new ESlint config. However, I should rewrite the pages to reduce
the number of statements.
---
src/components/templates/layout/layout.tsx | 60 ++++++++-----
src/i18n/en.json | 12 ++-
src/i18n/fr.json | 12 ++-
src/pages/404.tsx | 22 +++--
src/pages/article/[slug].tsx | 65 +++++++-------
src/pages/blog/index.tsx | 71 ++++++++-------
src/pages/blog/page/[number].tsx | 51 ++++++-----
src/pages/contact.tsx | 102 +++++++++++++---------
src/pages/cv.tsx | 133 ++++++++++++++--------------
src/pages/index.tsx | 129 +++++++++++++--------------
src/pages/mentions-legales.tsx | 36 +++++---
src/pages/projets/[slug].tsx | 134 +++++++++++++++--------------
src/pages/projets/index.tsx | 37 +++++---
src/pages/recherche/index.tsx | 54 +++++++-----
src/pages/sujet/[slug].tsx | 63 +++++++-------
src/pages/thematique/[slug].tsx | 46 ++++++----
src/utils/constants.ts | 32 +++++++
src/utils/helpers/pages.ts | 39 +++++----
src/utils/helpers/rss.ts | 19 ++--
src/utils/helpers/schema-org.ts | 13 +--
src/utils/hooks/use-breadcrumb.ts | 107 +++++++++++++++++++++++
src/utils/hooks/use-breadcrumb.tsx | 105 ----------------------
22 files changed, 747 insertions(+), 595 deletions(-)
create mode 100644 src/utils/constants.ts
create mode 100644 src/utils/hooks/use-breadcrumb.ts
delete mode 100644 src/utils/hooks/use-breadcrumb.tsx
(limited to 'src')
diff --git a/src/components/templates/layout/layout.tsx b/src/components/templates/layout/layout.tsx
index beb6562..7c97901 100644
--- a/src/components/templates/layout/layout.tsx
+++ b/src/components/templates/layout/layout.tsx
@@ -1,8 +1,16 @@
+/* eslint-disable max-statements */
import Script from 'next/script';
-import { FC, ReactElement, ReactNode, useRef, useState } from 'react';
+import {
+ type FC,
+ type ReactElement,
+ type ReactNode,
+ useRef,
+ useState,
+} from 'react';
import { useIntl } from 'react-intl';
-import { Person, SearchAction, WebSite, WithContext } from 'schema-dts';
-import { type NextPageWithLayoutOptions } from '../../../types';
+import type { Person, SearchAction, WebSite, WithContext } from 'schema-dts';
+import type { NextPageWithLayoutOptions } from '../../../types';
+import { ROUTES } from '../../../utils/constants';
import {
useRouteChange,
useScrollPosition,
@@ -25,7 +33,6 @@ import {
Header,
type HeaderProps,
} from '../../organisms';
-import photo from '/public/armand-philippot.jpg';
import styles from './layout.module.scss';
export type QueryAction = SearchAction & {
@@ -121,25 +128,25 @@ export const Layout: FC = ({
{
id: 'blog',
label: blogLabel,
- href: '/blog',
+ href: ROUTES.BLOG,
logo: ,
},
{
id: 'projects',
label: projectsLabel,
- href: '/projets',
+ href: ROUTES.PROJECTS,
logo: ,
},
{
id: 'cv',
label: cvLabel,
- href: '/cv',
+ href: ROUTES.CV,
logo: ,
},
{
id: 'contact',
label: contactLabel,
- href: '/contact',
+ href: ROUTES.CONTACT,
logo: ,
},
];
@@ -151,14 +158,14 @@ export const Layout: FC = ({
});
const footerNav: FooterProps['navItems'] = [
- { id: 'legal-notice', label: legalNoticeLabel, href: '/mentions-legales' },
+ { id: 'legal-notice', label: legalNoticeLabel, href: ROUTES.LEGAL_NOTICE },
];
const searchActionSchema: QueryAction = {
'@type': 'SearchAction',
target: {
'@type': 'EntryPoint',
- urlTemplate: `${url}/recherche?s={search_term_string}`,
+ urlTemplate: `${url}${ROUTES.SEARCH}?s={search_term_string}`,
},
query: 'required',
'query-input': 'required name=search_term_string',
@@ -168,9 +175,9 @@ export const Layout: FC = ({
'@context': 'https://schema.org',
'@id': `${url}`,
'@type': 'WebSite',
- name: name,
+ name,
description: baseline,
- url: url,
+ url,
author: { '@id': `${url}/#branding` },
copyrightYear: Number(copyright.start),
creator: { '@id': `${url}/#branding` },
@@ -183,10 +190,10 @@ export const Layout: FC = ({
'@context': 'https://schema.org',
'@type': 'Person',
'@id': `${url}/#branding`,
- name: name,
- url: url,
+ name,
+ url,
jobTitle: baseline,
- image: photo.src,
+ image: '/armand-philippot.jpg',
subjectOf: { '@id': `${url}` },
};
@@ -194,48 +201,56 @@ export const Layout: FC = ({
styles['back-to-top--hidden']
);
const updateBackToTopClassName = () => {
+ const visibleBreakpoint = 300;
setBackToTopClassName(
- window.scrollY > 300
+ window.scrollY > visibleBreakpoint
? styles['back-to-top--visible']
: styles['back-to-top--hidden']
);
};
useScrollPosition(updateBackToTopClassName);
+
const topRef = useRef(null);
const giveFocusToTopRef = () => {
if (topRef.current) topRef.current.focus();
};
+
useRouteChange(giveFocusToTopRef);
return (
<>
-
+
{skipToContent}
@@ -254,6 +269,7 @@ export const Layout: FC = ({
topId="top"
/>
>
@@ -270,6 +286,4 @@ export const Layout: FC = ({
export const getLayout = (
page: ReactElement,
props: NextPageWithLayoutOptions
-) => {
- return {page};
-};
+) => {page};
diff --git a/src/i18n/en.json b/src/i18n/en.json
index 02952b4..277ed23 100644
--- a/src/i18n/en.json
+++ b/src/i18n/en.json
@@ -143,6 +143,10 @@
"defaultMessage": "Contact",
"description": "Layout: main nav - contact link"
},
+ "AN9iy7": {
+ "defaultMessage": "Contact",
+ "description": "ContactPage: page title"
+ },
"AuGklx": {
"defaultMessage": "License:",
"description": "Meta: license label"
@@ -203,10 +207,6 @@
"defaultMessage": "Topics",
"description": "Error404Page: topics list widget title"
},
- "Gnf1Si": {
- "defaultMessage": "{starsCount, plural, =0 {No stars on Github} one {# star on Github} other {# stars on Github}}",
- "description": "Projets: Github stars count"
- },
"HFdzae": {
"defaultMessage": "Contact form",
"description": "ContactForm: form accessible name"
@@ -559,6 +559,10 @@
"defaultMessage": "No comments.",
"description": "PageLayout: no comments text"
},
+ "sI7gJK": {
+ "defaultMessage": "{starsCount, plural, =0 {No stars on Github} one {# star on Github} other {# stars on Github}}",
+ "description": "ProjectsPage: Github stars count"
+ },
"sO/Iwj": {
"defaultMessage": "Contact me",
"description": "HomePage: contact button text"
diff --git a/src/i18n/fr.json b/src/i18n/fr.json
index 2ec3657..69f6b42 100644
--- a/src/i18n/fr.json
+++ b/src/i18n/fr.json
@@ -143,6 +143,10 @@
"defaultMessage": "Contact",
"description": "Layout: main nav - contact link"
},
+ "AN9iy7": {
+ "defaultMessage": "Contact",
+ "description": "ContactPage: page title"
+ },
"AuGklx": {
"defaultMessage": "Licence :",
"description": "Meta: license label"
@@ -203,10 +207,6 @@
"defaultMessage": "Sujets",
"description": "Error404Page: topics list widget title"
},
- "Gnf1Si": {
- "defaultMessage": "{starsCount, plural, =0 {0 étoile sur Github} one {# étoile sur Github} other {# étoiles sur Github}}",
- "description": "Projets: Github stars count"
- },
"HFdzae": {
"defaultMessage": "Formulaire de contact",
"description": "ContactForm: form accessible name"
@@ -559,6 +559,10 @@
"defaultMessage": "Aucun commentaire.",
"description": "PageLayout: no comments text"
},
+ "sI7gJK": {
+ "defaultMessage": "{starsCount, plural, =0 {0 étoile sur Github} one {# étoile sur Github} other {# étoiles sur Github}}",
+ "description": "ProjectsPage: Github stars count"
+ },
"sO/Iwj": {
"defaultMessage": "Me contacter",
"description": "HomePage: contact button text"
diff --git a/src/pages/404.tsx b/src/pages/404.tsx
index 67daae1..af95a36 100644
--- a/src/pages/404.tsx
+++ b/src/pages/404.tsx
@@ -1,6 +1,6 @@
-import { GetStaticProps } from 'next';
+import type { GetStaticProps } from 'next';
import Head from 'next/head';
-import { ReactNode } from 'react';
+import type { ReactNode } from 'react';
import { useIntl } from 'react-intl';
import {
getLayout,
@@ -15,11 +15,12 @@ import {
getTotalThematics,
getTotalTopics,
} from '../services/graphql';
-import {
- type NextPageWithLayout,
- type RawThematicPreview,
- type RawTopicPreview,
+import type {
+ NextPageWithLayout,
+ RawThematicPreview,
+ RawTopicPreview,
} from '../types';
+import { ROUTES } from '../utils/constants';
import { getLinksListItems, getPageLinkFromRawData } from '../utils/helpers';
import { loadTranslation, type Messages } from '../utils/helpers/server';
import { useBreadcrumb, useSettings } from '../utils/hooks';
@@ -52,12 +53,12 @@ const Error404Page: NextPageWithLayout = ({
description: 'Error404Page: page body',
},
{
- link: (chunks: ReactNode) => {chunks},
+ link: (chunks: ReactNode) => {chunks},
}
);
const { items: breadcrumbItems, schema: breadcrumbSchema } = useBreadcrumb({
title,
- url: `/404`,
+ url: ROUTES.NOT_FOUND,
});
const pageTitle = intl.formatMessage(
{
@@ -88,6 +89,7 @@ const Error404Page: NextPageWithLayout = ({
<>
{pageTitle}
+ {/*eslint-disable-next-line react/jsx-no-literals -- Name allowed */}
= ({
breadcrumbSchema={breadcrumbSchema}
widgets={[
@@ -106,6 +109,7 @@ const Error404Page: NextPageWithLayout = ({
level={2}
/>,
getPageLinkFromRawData(topic, 'topic'))
@@ -123,7 +127,7 @@ const Error404Page: NextPageWithLayout = ({
id: 'XKy7rx',
})}
-
+
>
);
diff --git a/src/pages/article/[slug].tsx b/src/pages/article/[slug].tsx
index f564f35..9ecd8e1 100644
--- a/src/pages/article/[slug].tsx
+++ b/src/pages/article/[slug].tsx
@@ -1,9 +1,10 @@
-import { GetStaticPaths, GetStaticProps } from 'next';
+/* eslint-disable max-statements */
+import type { ParsedUrlQuery } from 'querystring';
+import type { GetStaticPaths, GetStaticProps } from 'next';
import Head from 'next/head';
import { useRouter } from 'next/router';
import Script from 'next/script';
-import { ParsedUrlQuery } from 'querystring';
-import { HTMLAttributes } from 'react';
+import type { HTMLAttributes } from 'react';
import { useIntl } from 'react-intl';
import {
ButtonLink,
@@ -21,11 +22,8 @@ import {
getArticleBySlug,
} from '../../services/graphql';
import styles from '../../styles/pages/article.module.scss';
-import {
- type Article,
- type NextPageWithLayout,
- type SingleComment,
-} from '../../types';
+import type { Article, NextPageWithLayout, SingleComment } from '../../types';
+import { ROUTES } from '../../utils/constants';
import {
getBlogSchema,
getSchemaJson,
@@ -66,17 +64,17 @@ const ArticlePage: NextPageWithLayout = ({
fallback: comments,
});
const { items: breadcrumbItems, schema: breadcrumbSchema } = useBreadcrumb({
- title: article?.title || '',
- url: `/article/${slug}`,
+ title: article?.title ?? '',
+ url: `${ROUTES.ARTICLE}/${slug}`,
});
- const readingTime = useReadingTime(article?.meta.wordsCount || 0, true);
+ const readingTime = useReadingTime(article?.meta.wordsCount ?? 0, true);
const { website } = useSettings();
const prismPlugins: OptionalPrismPlugin[] = ['command-line', 'line-numbers'];
const { attributes, className } = usePrism({ plugins: prismPlugins });
- if (isFallback) return ;
+ if (isFallback || !article) return ;
- const { content, id, intro, meta, title } = article!;
+ const { content, id, intro, meta, title } = article;
const { author, commentsCount, cover, dates, seo, thematics, topics } = meta;
const headerMeta: PageLayoutProps['headerMeta'] = {
@@ -87,13 +85,13 @@ const ArticlePage: NextPageWithLayout = ({
? { date: dates.update }
: undefined,
readingTime,
- thematics:
- thematics &&
- thematics.map((thematic) => (
-
- {thematic.name}
-
- )),
+ thematics: thematics
+ ? thematics.map((thematic) => (
+
+ {thematic.name}
+
+ ))
+ : undefined,
};
const footerMetaLabel = intl.formatMessage({
@@ -105,13 +103,11 @@ const ArticlePage: NextPageWithLayout = ({
const footerMeta: PageLayoutProps['footerMeta'] = {
custom: topics && {
label: footerMetaLabel,
- value: topics.map((topic) => {
- return (
-
- {topic.logo && } {topic.name}
-
- );
- }),
+ value: topics.map((topic) => (
+
+ {topic.logo ? : null} {topic.name}
+
+ )),
},
};
@@ -160,7 +156,7 @@ const ArticlePage: NextPageWithLayout = ({
*/
const prismClassNameReplacer = (str: string): string => {
const wpBlockClassName = 'wp-block-code';
- const languageArray = str.match(/language-[^\s|"]+/);
+ const languageArray = /language-[^\s|"]+/.exec(str);
const languageClassName = languageArray ? `${languageArray[0]}` : '';
if (
@@ -184,15 +180,19 @@ const ArticlePage: NextPageWithLayout = ({
<>
{seo.title}
+ {/*eslint-disable-next-line react/jsx-no-literals -- Name allowed */}
-
+
+ {/*eslint-disable-next-line react/jsx-no-literals -- Content allowed */}
= ({
withToC={true}
widgets={[
= ({
ArticlePage.getLayout = (page) => getLayout(page, { useGrid: true });
-interface PostParams extends ParsedUrlQuery {
+type PostParams = {
slug: string;
-}
+} & ParsedUrlQuery;
export const getStaticProps: GetStaticProps = async ({
locale,
params,
}) => {
- const post = await getArticleBySlug(params!.slug as PostParams['slug']);
+ const post = await getArticleBySlug((params as PostParams).slug);
const comments = await getAllComments({ contentId: post.id as number });
const translation = await loadTranslation(locale);
diff --git a/src/pages/blog/index.tsx b/src/pages/blog/index.tsx
index 13a4c57..7f6c540 100644
--- a/src/pages/blog/index.tsx
+++ b/src/pages/blog/index.tsx
@@ -1,7 +1,9 @@
-import { GetStaticProps } from 'next';
+/* eslint-disable max-statements */
+import type { GetStaticProps } from 'next';
import Head from 'next/head';
import { useRouter } from 'next/router';
import Script from 'next/script';
+import { useCallback } from 'react';
import { useIntl } from 'react-intl';
import {
getLayout,
@@ -18,14 +20,15 @@ import {
getTotalThematics,
getTotalTopics,
} from '../../services/graphql';
-import {
- type EdgesResponse,
- type NextPageWithLayout,
- type RawArticle,
- type RawThematicPreview,
- type RawTopicPreview,
+import type {
+ EdgesResponse,
+ NextPageWithLayout,
+ RawArticle,
+ RawThematicPreview,
+ RawTopicPreview,
} from '../../types';
import { settings } from '../../utils/config';
+import { ROUTES } from '../../utils/constants';
import {
getBlogSchema,
getLinksListItems,
@@ -62,19 +65,22 @@ const BlogPage: NextPageWithLayout = ({
});
const { items: breadcrumbItems, schema: breadcrumbSchema } = useBreadcrumb({
title,
- url: '/blog',
+ url: ROUTES.BLOG,
});
const { blog, website } = useSettings();
const { asPath } = useRouter();
- const pageTitle = intl.formatMessage(
- {
- defaultMessage: 'Blog: development, open source - {websiteName}',
- description: 'BlogPage: SEO - Page title',
- id: '+Y+tLK',
- },
- { websiteName: website.name }
- );
+ const page = {
+ title: intl.formatMessage(
+ {
+ defaultMessage: 'Blog: development, open source - {websiteName}',
+ description: 'BlogPage: SEO - Page title',
+ id: '+Y+tLK',
+ },
+ { websiteName: website.name }
+ ),
+ url: `${website.url}${asPath}`,
+ };
const pageDescription = intl.formatMessage(
{
defaultMessage:
@@ -110,12 +116,9 @@ const BlogPage: NextPageWithLayout = ({
perPage: blog.postsPerPage,
});
- /**
- * Load more posts handler.
- */
- const loadMore = () => {
+ const loadMore = useCallback(() => {
setSize((prevSize) => prevSize + 1);
- };
+ }, [setSize]);
const thematicsListTitle = intl.formatMessage({
defaultMessage: 'Thematics',
@@ -128,20 +131,25 @@ const BlogPage: NextPageWithLayout = ({
description: 'BlogPage: topics list widget title',
id: '2D9tB5',
});
+ const postsListBaseUrl = `${ROUTES.BLOG}/page/`;
return (
<>
- {pageTitle}
+ {page.title}
+ {/*eslint-disable-next-line react/jsx-no-literals -- Name allowed */}
-
+
+ {/*eslint-disable-next-line react/jsx-no-literals -- Content allowed */}
= ({
headerMeta={{ total: totalArticles }}
widgets={[
@@ -161,6 +170,7 @@ const BlogPage: NextPageWithLayout = ({
level={2}
/>,
getPageLinkFromRawData(topic, 'topic'))
@@ -170,20 +180,21 @@ const BlogPage: NextPageWithLayout = ({
/>,
]}
>
- {data && (
+ {data ? (
- )}
- {error && (
+ ) : null}
+ {error ? (
= ({
id: 'C/XGkH',
})}
/>
- )}
+ ) : null}
>
);
diff --git a/src/pages/blog/page/[number].tsx b/src/pages/blog/page/[number].tsx
index 4eaade5..b63fa9b 100644
--- a/src/pages/blog/page/[number].tsx
+++ b/src/pages/blog/page/[number].tsx
@@ -1,8 +1,9 @@
-import { GetStaticPaths, GetStaticProps } from 'next';
+/* eslint-disable max-statements */
+import type { ParsedUrlQuery } from 'querystring';
+import type { GetStaticPaths, GetStaticProps } from 'next';
import Head from 'next/head';
import { useRouter } from 'next/router';
import Script from 'next/script';
-import { ParsedUrlQuery } from 'querystring';
import { useIntl } from 'react-intl';
import {
getLayout,
@@ -19,12 +20,12 @@ import {
getTotalThematics,
getTotalTopics,
} from '../../../services/graphql';
-import {
- type EdgesResponse,
- type NextPageWithLayout,
- type RawArticle,
- type RawThematicPreview,
- type RawTopicPreview,
+import type {
+ EdgesResponse,
+ NextPageWithLayout,
+ RawArticle,
+ RawThematicPreview,
+ RawTopicPreview,
} from '../../../types';
import { settings } from '../../../utils/config';
import {
@@ -41,6 +42,7 @@ import {
useRedirection,
useSettings,
} from '../../../utils/hooks';
+import { ROUTES } from 'src/utils/constants';
type BlogPageProps = {
articles: EdgesResponse;
@@ -63,7 +65,7 @@ const BlogPage: NextPageWithLayout = ({
}) => {
useRedirection({
query: { param: 'number', value: '1' },
- redirectTo: '/blog',
+ redirectTo: ROUTES.BLOG,
});
const intl = useIntl();
@@ -85,12 +87,15 @@ const BlogPage: NextPageWithLayout = ({
const pageTitleWithPageNumber = `${title} - ${pageNumberTitle}`;
const { items: breadcrumbItems, schema: breadcrumbSchema } = useBreadcrumb({
title: pageNumberTitle,
- url: `/blog/page/${pageNumber}`,
+ url: `${ROUTES.BLOG}/page/${pageNumber}`,
});
const { website } = useSettings();
const { asPath } = useRouter();
- const pageTitle = `${pageTitleWithPageNumber} - ${website.name}`;
+ const page = {
+ title: `${pageTitleWithPageNumber} - ${website.name}`,
+ url: `${website.url}${asPath}`,
+ };
const pageDescription = intl.formatMessage(
{
defaultMessage:
@@ -124,20 +129,25 @@ const BlogPage: NextPageWithLayout = ({
description: 'BlogPage: topics list widget title',
id: '2D9tB5',
});
+ const postsListBaseUrl = `${ROUTES.BLOG}/page/`;
return (
<>
- {pageTitle}
+ {page.title}
+ {/*eslint-disable-next-line react/jsx-no-literals -- Name allowed */}
-
+
+ {/*eslint-disable-next-line react/jsx-no-literals -- Content allowed */}
= ({
headerMeta={{ total: totalArticles }}
widgets={[
@@ -157,6 +168,7 @@ const BlogPage: NextPageWithLayout = ({
level={2}
/>,
getPageLinkFromRawData(topic, 'topic'))
@@ -167,11 +179,11 @@ const BlogPage: NextPageWithLayout = ({
]}
>
@@ -182,18 +194,17 @@ const BlogPage: NextPageWithLayout = ({
BlogPage.getLayout = (page) =>
getLayout(page, { useGrid: true, withExtraPadding: true });
-interface BlogPageParams extends ParsedUrlQuery {
+type BlogPageParams = {
number: string;
-}
+} & ParsedUrlQuery;
export const getStaticProps: GetStaticProps = async ({
locale,
params,
}) => {
- const pageNumber = Number(params!.number as BlogPageParams['number']);
- const queriedPostsNumber = settings.postsPerPage * pageNumber;
+ const pageNumber = Number((params as BlogPageParams).number);
const lastCursor = await getArticlesEndCursor({
- first: queriedPostsNumber,
+ first: settings.postsPerPage * pageNumber,
});
const articles = await getArticles({
first: settings.postsPerPage,
diff --git a/src/pages/contact.tsx b/src/pages/contact.tsx
index 92c58cc..d187a93 100644
--- a/src/pages/contact.tsx
+++ b/src/pages/contact.tsx
@@ -1,8 +1,9 @@
-import { GetStaticProps } from 'next';
+/* eslint-disable max-statements */
+import type { GetStaticProps } from 'next';
import Head from 'next/head';
import { useRouter } from 'next/router';
import Script from 'next/script';
-import { useState } from 'react';
+import { useCallback, useState } from 'react';
import { useIntl } from 'react-intl';
import {
ContactForm,
@@ -16,7 +17,8 @@ import {
import { meta } from '../content/pages/contact.mdx';
import { sendMail } from '../services/graphql';
import styles from '../styles/pages/contact.module.scss';
-import { type NextPageWithLayout } from '../types';
+import type { NextPageWithLayout } from '../types';
+import { ROUTES } from '../utils/constants';
import {
getSchemaJson,
getSinglePageSchema,
@@ -30,9 +32,14 @@ const ContactPage: NextPageWithLayout = () => {
const intl = useIntl();
const { items: breadcrumbItems, schema: breadcrumbSchema } = useBreadcrumb({
title,
- url: `/contact`,
+ url: ROUTES.CONTACT,
});
+ const pageTitle = intl.formatMessage({
+ defaultMessage: 'Contact',
+ description: 'ContactPage: page title',
+ id: 'AN9iy7',
+ });
const socialMediaTitle = intl.formatMessage({
defaultMessage: 'Find me elsewhere',
description: 'ContactPage: social media widget title',
@@ -61,6 +68,7 @@ const ContactPage: NextPageWithLayout = () => {
const widgets = [
{
/>,
];
- const [status, setStatus] = useState('info');
+ const [statusKind, setStatusKind] = useState('info');
const [statusMessage, setStatusMessage] = useState('');
- const submitMail: ContactFormProps['sendMail'] = async (data, reset) => {
- const { email, message, name, object } = data;
- const messageHTML = message.replace(/\r?\n/g, '
');
- const body = `Message received from ${name} <${email}> on ${website.url}.
${messageHTML}`;
- const replyTo = `${name} <${email}>`;
- const mailData = {
- body,
- clientMutationId: 'contact',
- replyTo,
- subject: object,
- };
- const { message: mutationMessage, sent } = await sendMail(mailData);
+ const submitMail: ContactFormProps['sendMail'] = useCallback(
+ async (data, reset) => {
+ const { email, message, name, object } = data;
+ const messageHTML = message.replace(/\r?\n/g, '
');
+ const body = `Message received from ${name} <${email}> on ${website.url}.
${messageHTML}`;
+ const replyTo = `${name} <${email}>`;
+ const mailData = {
+ body,
+ clientMutationId: 'contact',
+ replyTo,
+ subject: object,
+ };
+ const { message: mutationMessage, sent } = await sendMail(mailData);
- if (sent) {
- setStatus('success');
- setStatusMessage(
- intl.formatMessage({
- defaultMessage:
- 'Thanks. Your message was successfully sent. I will answer it as soon as possible.',
- description: 'Contact: success message',
- id: '3Pipok',
- })
- );
- reset();
- } else {
- const errorPrefix = intl.formatMessage({
- defaultMessage: 'An error occurred:',
- description: 'Contact: error message',
- id: 'TpyFZ6',
- });
- const error = `${errorPrefix} ${mutationMessage}`;
- setStatus('error');
- setStatusMessage(error);
- }
+ if (sent) {
+ setStatusKind('success');
+ setStatusMessage(
+ intl.formatMessage({
+ defaultMessage:
+ 'Thanks. Your message was successfully sent. I will answer it as soon as possible.',
+ description: 'Contact: success message',
+ id: '3Pipok',
+ })
+ );
+ reset();
+ } else {
+ const errorPrefix = intl.formatMessage({
+ defaultMessage: 'An error occurred:',
+ description: 'Contact: error message',
+ id: 'TpyFZ6',
+ });
+ const error = `${errorPrefix} ${mutationMessage}`;
+ setStatusKind('error');
+ setStatusMessage(error);
+ }
+ },
+ [intl, website.url]
+ );
+ const page = {
+ title: `${seo.title} - ${website.name}`,
+ url: `${website.url}${asPath}`,
};
return (
<>
- {`${seo.title} - ${website.name}`}
+ {page.title}
+ {/*eslint-disable-next-line react/jsx-no-literals -- Name allowed */}
-
+
+ {/*eslint-disable-next-line react/jsx-no-literals -- Content allowed */}