aboutsummaryrefslogtreecommitdiffstats
path: root/src/pages/index.tsx
diff options
context:
space:
mode:
authorArmand Philippot <git@armandphilippot.com>2023-11-29 18:07:20 +0100
committerArmand Philippot <git@armandphilippot.com>2023-11-29 18:07:20 +0100
commitd363306235f2a48f16e488f20f73e2233ddcf281 (patch)
tree5e86a7b5f38416d7ee56a9aff5ef972aa73d82b1 /src/pages/index.tsx
parentdfa894b76ee3584bf169710c78c57330c5d6ee67 (diff)
refactor(pages): improve Homepage
* move custom homepage components that does not require props to the MDX file (links should not need to be translated here but where they are defined) * move SEO title and meta desc to MDX file * make Page component the wrapper instead of using a React fragment * fix MDX module types
Diffstat (limited to 'src/pages/index.tsx')
-rw-r--r--src/pages/index.tsx347
1 files changed, 76 insertions, 271 deletions
diff --git a/src/pages/index.tsx b/src/pages/index.tsx
index 7bd8aec..f4d36c1 100644
--- a/src/pages/index.tsx
+++ b/src/pages/index.tsx
@@ -3,10 +3,9 @@ import type { GetStaticProps } from 'next';
import Head from 'next/head';
import NextImage from 'next/image';
import Script from 'next/script';
-import type { FC, HTMLAttributes, ReactNode } from 'react';
+import type { FC } from 'react';
import { useIntl } from 'react-intl';
import {
- ButtonLink,
Card,
CardCover,
CardFooter,
@@ -15,261 +14,77 @@ import {
CardTitle,
getLayout,
Grid,
- Icon,
- List,
- ListItem,
Time,
MetaItem,
- type PageSectionProps,
- PageSection,
Page,
} from '../components';
import { mdxComponents } from '../components/mdx';
-import HomePageContent from '../content/pages/homepage.mdx';
+import HomePageContent, { meta } from '../content/pages/homepage.mdx';
import {
convertRecentPostToRecentArticle,
fetchRecentPosts,
} from '../services/graphql';
-import styles from '../styles/pages/home.module.scss';
import type { NextPageWithLayout, RecentArticle } from '../types';
import { CONFIG } from '../utils/config';
-import { PERSONAL_LINKS, ROUTES } from '../utils/constants';
+import { ROUTES } from '../utils/constants';
import { getSchemaJson, getWebPageSchema } from '../utils/helpers';
import { loadTranslation, type Messages } from '../utils/helpers/server';
import { useBreadcrumb } from '../utils/hooks';
-/**
- * Column component.
- *
- * Render the body as a column.
- */
-const Column = ({ children, ...props }: HTMLAttributes<HTMLDivElement>) => (
- <div {...props}>{children}</div>
-);
-
-/**
- * Retrieve a list of coding links.
- *
- * @returns {JSX.Element} - A list of links.
- */
-const CodingLinks: FC = () => {
- const intl = useIntl();
-
- return (
- <List className={styles.list} hideMarker isInline spacing="sm">
- <ListItem>
- <ButtonLink to={ROUTES.THEMATICS.WEB_DEV}>
- {intl.formatMessage({
- defaultMessage: 'Web development',
- description: 'HomePage: link to web development thematic',
- id: 'vkF/RP',
- })}
- </ButtonLink>
- </ListItem>
- <ListItem>
- <ButtonLink to={ROUTES.PROJECTS}>
- {intl.formatMessage({
- defaultMessage: 'Projects',
- description: 'HomePage: link to projects',
- id: 'N44SOc',
- })}
- </ButtonLink>
- </ListItem>
- </List>
- );
-};
-
-/**
- * Retrieve a list of Coldark repositories.
- *
- * @returns {JSX.Element} - A list of links.
- */
-const ColdarkRepos: FC = () => {
- const intl = useIntl();
- const repo = {
- github: 'https://github.com/ArmandPhilippot/coldark',
- gitlab: 'https://gitlab.com/ArmandPhilippot/coldark',
- };
-
- return (
- <List className={styles.list} hideMarker isInline spacing="sm">
- <ListItem>
- <ButtonLink isExternal to={repo.github}>
- {intl.formatMessage({
- defaultMessage: 'Github',
- description: 'HomePage: Github link',
- id: '3f3PzH',
- })}
- </ButtonLink>
- </ListItem>
- <ListItem>
- <ButtonLink isExternal to={repo.gitlab}>
- {intl.formatMessage({
- defaultMessage: 'Gitlab',
- description: 'HomePage: Gitlab link',
- id: '7AnwZ7',
- })}
- </ButtonLink>
- </ListItem>
- </List>
- );
-};
-
-/**
- * Retrieve a list of links related to Free thematic.
- *
- * @returns {JSX.Element} - A list of links.
- */
-const LibreLinks: FC = () => {
- const intl = useIntl();
-
- return (
- <List className={styles.list} hideMarker isInline spacing="sm">
- <ListItem>
- <ButtonLink to={ROUTES.THEMATICS.FREE}>
- {intl.formatMessage({
- defaultMessage: 'Free',
- description: 'HomePage: link to free thematic',
- id: 'w8GrOf',
- })}
- </ButtonLink>
- </ListItem>
- <ListItem>
- <ButtonLink to={ROUTES.THEMATICS.LINUX}>
- {intl.formatMessage({
- defaultMessage: 'Linux',
- description: 'HomePage: link to Linux thematic',
- id: 'jASD7k',
- })}
- </ButtonLink>
- </ListItem>
- </List>
- );
-};
-
-/**
- * Retrieve the Shaarli link.
- *
- * @returns {JSX.Element} - A list of links
- */
-const ShaarliLink: FC = () => {
- const intl = useIntl();
-
- return (
- <List className={styles.list} hideMarker isInline spacing="sm">
- <ListItem>
- <ButtonLink isExternal to={PERSONAL_LINKS.SHAARLI}>
- {intl.formatMessage({
- defaultMessage: 'Shaarli',
- description: 'HomePage: link to Shaarli',
- id: 'i5L19t',
- })}
- </ButtonLink>
- </ListItem>
- </List>
- );
-};
-
-/**
- * Retrieve the additional links.
- *
- * @returns {JSX.Element} - A list of links.
- */
-const MoreLinks: FC = () => {
- const intl = useIntl();
-
- return (
- <List className={styles.list} hideMarker isInline spacing="sm">
- <ListItem>
- <ButtonLink to={ROUTES.CONTACT}>
- <Icon aria-hidden={true} shape="envelop" />
- {intl.formatMessage({
- defaultMessage: 'Contact me',
- description: 'HomePage: contact button text',
- id: 'sO/Iwj',
- })}
- </ButtonLink>
- </ListItem>
- <ListItem>
- <ButtonLink to={ROUTES.RSS}>
- <Icon aria-hidden={true} shape="feed" />
- {intl.formatMessage({
- defaultMessage: 'Subscribe',
- description: 'HomePage: RSS feed subscription text',
- id: 'T4YA64',
- })}
- </ButtonLink>
- </ListItem>
- </List>
- );
+type RecentPostsProps = {
+ posts: RecentArticle[];
};
-const StyledGrid = ({ children }: { children: ReactNode }) => (
- <Grid className={styles.columns} gap="sm" sizeMin="250px">
- {children}
- </Grid>
-);
-
/**
- * Create the page sections.
+ * Get a cards list of recent posts.
*
- * @param {object} obj - An object containing the section body.
- * @param {ReactNode[]} obj.children - The section body.
- * @returns {JSX.Element} A section element.
- */
-const HomePageSection: FC<PageSectionProps> = ({
- children,
- hasBorder = true,
- variant,
-}) => (
- <PageSection
- className={styles.section}
- hasBorder={hasBorder}
- variant={variant}
- >
- {children}
- </PageSection>
-);
-
-type HomeProps = {
- recentPosts: RecentArticle[];
- translation?: Messages;
-};
-
-/**
- * Home page.
+ * @returns {JSX.Element} - The cards list.
*/
-const HomePage: NextPageWithLayout<HomeProps> = ({ recentPosts }) => {
+const RecentPosts: FC<RecentPostsProps> = ({ posts }): JSX.Element => {
const intl = useIntl();
const publicationDate = intl.formatMessage({
defaultMessage: 'Published on:',
description: 'HomePage: publication date label',
id: 'pT5nHk',
});
- const { schema: breadcrumbSchema } = useBreadcrumb({
- title: '',
- url: `/`,
- });
-
- /**
- * Get a cards list of recent posts.
- *
- * @returns {JSX.Element} - The cards list.
- */
- const getRecentPosts = (): JSX.Element => {
- const listClass = `${styles.list} ${styles['list--cards']}`;
- return (
- <Grid className={listClass} gap="sm" isCentered sizeMax="25ch">
- {recentPosts.map((post) => (
+ return (
+ <Grid
+ // eslint-disable-next-line react/jsx-no-literals
+ gap="sm"
+ // eslint-disable-next-line react/jsx-no-literals
+ sizeMax="25ch"
+ >
+ {posts.map((post) => {
+ const postUrl = `${ROUTES.ARTICLE}/${post.slug}`;
+ const cardLabel = intl.formatMessage(
+ {
+ defaultMessage: 'View {pageTitle}',
+ description: 'RecentPosts: card accessible name',
+ id: 'mWZU4R',
+ },
+ {
+ pageTitle: post.title,
+ }
+ );
+ const coverLabel = intl.formatMessage(
+ {
+ defaultMessage: 'Cover of {pageTitle}',
+ description: 'RecentPosts: card cover accessible name',
+ id: 'kq+fzI',
+ },
+ {
+ pageTitle: post.title,
+ }
+ );
+
+ return (
<Card
+ aria-label={cardLabel}
cover={
post.cover ? (
- <CardCover hasBorders>
- <NextImage
- {...post.cover}
- style={{ objectFit: 'scale-down' }}
- />
+ <CardCover aria-label={coverLabel} hasBorders>
+ <NextImage {...post.cover} />
</CardCover>
) : undefined
}
@@ -285,65 +100,57 @@ const HomePage: NextPageWithLayout<HomeProps> = ({ recentPosts }) => {
</CardMeta>
}
isCentered
- linkTo={`${ROUTES.ARTICLE}/${post.slug}`}
+ linkTo={postUrl}
>
<CardHeader>
<CardTitle level={3}>{post.title}</CardTitle>
</CardHeader>
<CardFooter />
</Card>
- ))}
- </Grid>
- );
- };
+ );
+ })}
+ </Grid>
+ );
+};
- const components: MDXComponents = {
+const getComponents = (recentPosts: RecentArticle[]): MDXComponents => {
+ return {
...mdxComponents,
- CodingLinks,
- ColdarkRepos,
- Column,
- Grid: StyledGrid,
- LibreLinks,
- MoreLinks,
- RecentPosts: getRecentPosts,
- Section: HomePageSection,
- ShaarliLink,
+ RecentPosts: () => <RecentPosts posts={recentPosts} />,
};
+};
+
+type HomeProps = {
+ recentPosts: RecentArticle[];
+ translation?: Messages;
+};
+
+/**
+ * Home page.
+ */
+const HomePage: NextPageWithLayout<HomeProps> = ({ recentPosts }) => {
+ const { schema: breadcrumbSchema } = useBreadcrumb({
+ title: '',
+ url: ROUTES.HOME,
+ });
- const pageTitle = intl.formatMessage(
- {
- defaultMessage: '{websiteName} | Front-end developer: WordPress/React',
- description: 'HomePage: SEO - Page title',
- id: 'PXp2hv',
- },
- { websiteName: CONFIG.name }
- );
- const pageDescription = intl.formatMessage(
- {
- defaultMessage:
- '{websiteName} is a front-end developer located in France. He codes and he writes mostly about web development and open-source.',
- description: 'HomePage: SEO - Meta description',
- id: 'tMuNTy',
- },
- { websiteName: CONFIG.name }
- );
const webpageSchema = getWebPageSchema({
- description: pageDescription,
+ description: meta.seo.description,
locale: CONFIG.locales.defaultLocale,
- slug: '',
- title: pageTitle,
+ slug: ROUTES.HOME,
+ title: meta.seo.title,
});
const schemaJsonLd = getSchemaJson([webpageSchema]);
return (
- <>
+ <Page hasSections>
<Head>
- <title>{pageTitle}</title>
+ <title>{meta.seo.title}</title>
{/*eslint-disable-next-line react/jsx-no-literals -- Name allowed */}
- <meta name="description" content={pageDescription} />
+ <meta name="description" content={meta.seo.description} />
<meta property="og:url" content={CONFIG.url} />
- <meta property="og:title" content={pageTitle} />
- <meta property="og:description" content={pageDescription} />
+ <meta property="og:title" content={meta.seo.title} />
+ <meta property="og:description" content={meta.seo.description} />
</Head>
<Script
// eslint-disable-next-line react/jsx-no-literals -- Id allowed
@@ -357,10 +164,8 @@ const HomePage: NextPageWithLayout<HomeProps> = ({ recentPosts }) => {
type="application/ld+json"
dangerouslySetInnerHTML={{ __html: JSON.stringify(breadcrumbSchema) }}
/>
- <Page hasSections>
- <HomePageContent components={components} />
- </Page>
- </>
+ <HomePageContent components={getComponents(recentPosts)} />
+ </Page>
);
};